View source  
  <?php
if (module_exists('drush')) {
  include drupal_get_path('module', 'coder') . '/coder.drush.inc';
}
define('SEVERITY_MINOR', 1);
define('SEVERITY_NORMAL', 5);
define('SEVERITY_CRITICAL', 9);
function coder_help($page, $arg) {
  switch ($page) {
    case 'coder#disclaimer':
      return t('Coder provides helpful hints without false positives, but offers no guarantee for creating good code.  You are the final arbitrar.  If in doubt, read the Drupal documentation (see review links below and <a href="@api">api.drupal.org</a>).', array(
        '@api' => 'http://api.drupal.org',
      ));
    case 'drush:coder':
      return t('coder [summary] [@reviews] [minor|major|critical] [active|core|all|default|<modules>] [no-<module>]', array(
        '@reviews' => implode('|', array_keys(_coder_reviews())),
      ));
  }
}
function _coder_reviews() {
  return module_invoke_all('reviews');
}
function coder_reviews() {
  global $_coder_reviews;
  if (!isset($_coder_reviews)) {
    $_coder_reviews = array();
    $path = drupal_get_path('module', 'coder') . '/includes';
    $files = drupal_system_listing('coder_.*\\.inc$', $path, 'filename', 0);
    foreach ($files as $file) {
      require_once './' . $file->filename;
      $function = $file->name . '_reviews';
      if (function_exists($function)) {
        if ($review = call_user_func($function)) {
          $_coder_reviews = array_merge($_coder_reviews, $review);
        }
      }
    }
  }
  return $_coder_reviews;
}
function coder_perm() {
  return array(
    'view code review',
    'view code review all',
  );
}
function coder_menu() {
  $items = array();
  $items['coder'] = array(
    'title' => 'Code review',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['coder/settings'] = array(
    'title' => 'Selection form',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -2,
  );
  $items['coder/default'] = array(
    'title' => 'Default',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => -1,
  );
  $items['coder/core'] = array(
    'title' => 'Core',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['coder/active'] = array(
    'title' => 'Active',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['coder/all'] = array(
    'title' => 'All',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review all',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
  );
  $items['coder/patches'] = array(
    'title' => 'Patches',
    'page callback' => 'coder_page',
    'access arguments' => array(
      'view code review',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
  $items['admin/settings/coder'] = array(
    'title' => 'Code review',
    'description' => 'Select code review plugins and modules',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'coder_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  return $items;
}
function coder_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'system_modules' && (!isset($form['#theme']) || $form['#theme'] != 'confirm_form')) {
    if (user_access('view code review')) {
      $path = drupal_get_path('module', 'coder');
      drupal_add_css($path . '/coder.css', 'module');
      foreach ($form['name'] as $name => $data) {
        $description = isset($form['description'][$name]['#value']) ? $form['description'][$name]['#value'] : $data['#value'];
        $form['description'][$name]['#value'] = $description . ' (' . l(t('Code Review'), "coder/{$name}") . ')';
      }
    }
  }
}
function _coder_default_reviews() {
  return drupal_map_assoc(array(
    'style',
    'sql',
    'comment',
    'security',
  ));
}
function _coder_settings_form($settings, &$system, &$files) {
  
  $path = drupal_get_path('module', 'coder');
  drupal_add_js($path . '/coder.js');
  
  $reviews = _coder_reviews();
  foreach ($reviews as $name => $review) {
    $review_options[$name] = isset($review['#link']) ? l($review['#title'], $review['#link']) : $review['#title'];
    if (isset($review['#description'])) {
      $review_options[$name] .= ' (' . $review['#description'] . ')';
    }
    $review_sort[$name] = $review['#title'];
  }
  
  asort($review_sort);
  foreach ($review_sort as $name => $review) {
    $review_sort[$name] = $review_options[$name];
  }
  
  $form['coder_reviews_group'] = array(
    '#type' => 'fieldset',
    '#title' => t('Reviews'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['coder_reviews_group']['coder_reviews'] = array(
    '#type' => 'checkboxes',
    '#options' => $review_sort,
    '#description' => t('apply the checked coding reviews'),
    '#default_value' => $settings['coder_reviews'],
  );
  
  $form['coder_reviews_group']['coder_severity'] = array(
    '#type' => 'radios',
    '#options' => array(
      SEVERITY_MINOR => 'minor (most)',
      SEVERITY_NORMAL => 'normal',
      SEVERITY_CRITICAL => 'critical (fewest)',
    ),
    '#description' => t('show warnings at or above the severity warning level'),
    '#default_value' => $settings['coder_severity'],
  );
  if (!empty($settings['coder_patches'])) {
    
    $form['coder_what'] = array(
      '#type' => 'fieldset',
      '#title' => t('What to review'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
    );
    $form['coder_what']['coder_patch_help'] = array(
      '#value' => '<p>' . t('All patches must be in unified format.  It\'s also preferable for the patch to be created with the "-p" option, which shows the function closest to the code change.  Without this, some function dependent tests may not be triggered.  Coder offers no guarantee that the patch will apply cleanly or will function correctly.') . '</p>',
      '#weight' => -4,
    );
    $form['coder_what']['coder_patch_link'] = array(
      '#type' => 'textfield',
      '#title' => t('Link to patch'),
      '#default_value' => isset($settings['coder_patch_link']) ? $settings['coder_patch_link'] : '',
      '#weight' => -3,
    );
    $form['coder_what']['coder_patch_help'] = array(
      '#value' => '<p>' . t('Or') . '</p>',
      '#weight' => -2,
    );
    $form['coder_what']['coder_patch_text'] = array(
      '#type' => 'textarea',
      '#title' => t('Patch text'),
      '#rows' => 20,
      '#weight' => -1,
      '#default_value' => isset($settings['coder_patch_text']) ? $settings['coder_patch_text'] : '',
      '#attributes' => array(
        'wrap' => 'OFF',
      ),
    );
    $form['coder_patches'] = array(
      '#type' => 'value',
      '#value' => 1,
    );
    $in_patch = 0;
    $patch = $link_contents = $textarea_contents = '';
    $patches = array();
    if (!empty($settings['coder_patch_link'])) {
      $link_contents = file_get_contents($settings['coder_patch_link']);
    }
    if (!empty($settings['coder_patch_text'])) {
      $textarea_contents = $settings['coder_patch_text'];
    }
    $patch_contents = $link_contents . "\n" . $textarea_contents;
    $lines = preg_split("/(\r\n|\n)/", $patch_contents);
    foreach ($lines as $line) {
      if ($line == '') {
        continue;
      }
      if (preg_match("/^\\+\\+\\+\\s+([\\w\\.\\-\\/]+)\\s*.*?\$/", $line, $matches)) {
        if (!empty($patch)) {
          $patches[$filename . ": " . $patch_line_numbers] = $patch;
          $system[$filename . ": " . $patch_line_numbers] = $filename;
          $patch = '';
        }
        $filename = $matches[1];
      }
      elseif (preg_match("/^(@@\\s+\\-\\d+,\\d+\\s+\\+\\d+,\\d+\\s+@@)\\s*((function|(protected|private|public)*\\s*class)\\s([\\w]+).*?)*\$/", $line, $matches)) {
        if (!empty($patch)) {
          $patches[$filename . ": " . $patch_line_numbers] = $patch;
          $system[$filename . ": " . $patch_line_numbers] = $filename;
          $patch = '';
        }
        if (isset($matches[3]) && isset($matches[4])) {
          if ($matches[3] == 'function') {
            $function_current = $matches[4];
            $patch = 'function ' . $function_current . "() {\n";
          }
          else {
            $class_current = $matches[4];
            $patch = $matches[2] . "\n";
          }
        }
        $patch_line_numbers = $matches[1];
      }
      elseif (preg_match("/^\\+(?!\\+)/", $line)) {
        $patch .= ltrim($line, '+') . "\n";
        $in_patch = 1;
      }
      elseif (preg_match("/^\\s/", $line)) {
        $patch .= drupal_substr($line, 1) . "\n";
        $in_patch = 1;
      }
      else {
        $in_patch = 0;
      }
    }
    if (!empty($patch)) {
      $patches[$filename . ": " . $patch_line_numbers] = $patch;
      $system[$filename . ": " . $patch_line_numbers] = $filename;
      $patch = '';
    }
    $files = $patches;
  }
  else {
    
    $sql = 'SELECT name, filename, type, status FROM {system} WHERE type=\'module\' OR type=\'theme\' ORDER BY weight ASC, filename ASC';
    $result = db_query($sql);
    $system_modules = array();
    $system_themes = array();
    while ($system = db_fetch_object($result)) {
      $display_name = $system->name;
      if ($system->status) {
        $display_name .= ' (' . t('active') . ')';
        $system_active[$system->name] = $system->name;
      }
      if (_coder_is_drupal_core($system)) {
        $display_name .= ' (' . t('core') . ')';
        $system_core[$system->name] = $system->name;
      }
      if ($system->type == 'module') {
        $system_modules[$system->name] = $system->name;
      }
      else {
        $system_themes[$system->name] = $system->name;
      }
      $system_links[$system->name] = l($display_name, "coder/{$system->name}");
      $files[$system->name] = $system->filename;
    }
    asort($system_links);
    
    $form['coder_what'] = array(
      '#type' => 'fieldset',
      '#title' => t('What to review'),
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
    );
    
    $form['coder_what']['coder_active_modules'] = array(
      '#type' => 'checkbox',
      '#default_value' => isset($settings['coder_active_modules']) ? $settings['coder_active_modules'] : 0,
      '#title' => t('active modules and themes'),
    );
    $form['coder_what']['coder_core'] = array(
      '#type' => 'checkbox',
      '#default_value' => isset($settings['coder_core']) ? $settings['coder_core'] : 0,
      '#title' => t('core files (php, modules, and includes)'),
    );
    $ext = variable_get('coder_php_ext', array(
      'inc',
      'php',
      'install',
      'test',
    ));
    $form['coder_what']['coder_includes'] = array(
      '#type' => 'checkbox',
      '#default_value' => isset($settings['coder_includes']) ? $settings['coder_includes'] : 0,
      '#title' => t('include files (@ext)', array(
        '@ext' => implode(' | ', $ext),
      )),
    );
    $form['coder_what']['coder_includes_exclusion_fieldset'] = array(
      '#type' => 'fieldset',
      '#title' => t('include file exclusions'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['coder_what']['coder_includes_exclusion_fieldset']['coder_includes_exclusions'] = array(
      '#rows' => 3,
      '#type' => 'textarea',
      '#default_value' => isset($settings['coder_includes_exclusions']) ? $settings['coder_includes_exclusions'] : '',
      '#description' => t('List file names or paths, one per line, which should be excluded (only valid if "include files" is checked above).  For example, modules/system/*.php will exclude all php files in the modules/system directory.'),
    );
    
    $form['coder_what']['coder_modules'] = array(
      '#type' => 'fieldset',
      '#title' => t('Select specific modules'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      'checkboxes' => array(
        '#theme' => 'cols',
        '#cols' => 2,
      ),
    );
    if (isset($settings['coder_all'])) {
      $modules = $system_modules;
    }
    elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
      if (isset($settings['coder_core']) && $settings['coder_core']) {
        $modules = array_intersect($system_active, $system_core);
        $modules = array_intersect($modules, $system_modules);
      }
      else {
        $modules = array_intersect($system_active, $system_modules);
      }
    }
    elseif (isset($settings['coder_core']) && $settings['coder_core']) {
      $modules = array_intersect($system_core, $system_modules);
    }
    elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
      $modules = array_intersect($system_active, $system_modules);
    }
    elseif (isset($settings['coder_contrib']) && $settings['coder_contrib']) {
      $modules = array_diff($system_modules, array_intersect($system_core, $system_modules));
    }
    else {
      $modules = isset($settings['coder_modules']) && is_array($settings['coder_modules']) ? $settings['coder_modules'] : array();
    }
    
    $form['coder_what']['coder_themes'] = array(
      '#type' => 'fieldset',
      '#title' => t('Select specific themes'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      'checkboxes' => array(
        '#theme' => 'cols',
      ),
    );
    if (isset($settings['coder_all'])) {
      $themes = $system_themes;
    }
    elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
      if (isset($settings['coder_core']) && $settings['coder_core']) {
        $themes = array_intersect($system_active, $system_core);
        $themes = array_intersect($themes, $system_themes);
      }
      else {
        $themes = array_intersect($system_active, $system_themes);
      }
    }
    elseif (isset($settings['coder_core']) && $settings['coder_core']) {
      $themes = array_intersect($system_core, $system_themes);
    }
    elseif (isset($settings['coder_active_modules']) && $settings['coder_active_modules']) {
      $themes = array_intersect($system_active, $system_themes);
    }
    elseif (isset($settings['coder_contrib']) && $settings['coder_contrib']) {
      $themes = array_diff($system_themes, array_intersect($system_core, $system_themes));
    }
    else {
      $themes = isset($settings['coder_themes']) && is_array($settings['coder_themes']) ? $settings['coder_themes'] : array();
    }
    foreach ($system_links as $name => $link) {
      $classes = array();
      if (in_array($name, $system_active)) {
        $classes[] = 'coder-active';
      }
      if (in_array($name, $system_core)) {
        $classes[] = 'coder-core';
      }
      if (in_array($name, $system_themes)) {
        $type = 'theme';
        $default_value = isset($themes[$name]);
      }
      else {
        $type = 'module';
        $default_value = isset($modules[$name]);
      }
      $form['coder_what']["coder_{$type}s"]['checkboxes']["coder_{$type}s-{$name}"] = array(
        '#type' => 'checkbox',
        '#title' => $link,
        '#default_value' => $default_value,
        '#attributes' => array(
          'class' => implode(' ', $classes),
        ),
      );
    }
    $system = array_merge($modules, $themes);
  }
  return $form;
}
if (!function_exists('theme_cols')) {
  
  function theme_cols($form) {
    $total = 0;
    $cols = isset($form['#cols']) ? $form['#cols'] : 3;
    foreach ($form as $element_id => $element) {
      if ($element_id[0] != '#') {
        $total++;
      }
    }
    $total = (int) ($total % $cols ? ($total + $cols - 1) / $cols : $total / $cols);
    $pos = 0;
    $rows = array();
    foreach ($form as $element_id => $element) {
      if ($element_id[0] != '#') {
        $pos++;
        $row = $pos % $total;
        $col = $pos / $total;
        if (!isset($rows[$row])) {
          $rows[$row] = array();
        }
        $rows[$row][$col] = drupal_render($element);
      }
    }
    return theme('table', array(), $rows);
  }
}
function coder_admin_settings() {
  $settings = _coder_get_default_settings();
  $form = _coder_settings_form($settings, $system, $files);
  $form['help'] = array(
    '#type' => 'markup',
    '#value' => t('After setting these defaults, use <a href="@url">coder</a> to perform code reviews.', array(
      '@url' => url('coder'),
    )),
    '#weight' => -1,
  );
  $form['#submit'][] = 'coder_settings_form_submit';
  return system_settings_form($form);
}
function coder_settings_form_submit($form, &$form_state) {
  $form_state['storage'] = $form_state['values'];
  variable_set('coder_modules', _coder_settings_array($form_state, 'module'));
  variable_set('coder_themes', _coder_settings_array($form_state, 'theme'));
}
function _coder_settings_array(&$form_state, $type) {
  $typekey = "coder_{$type}s-";
  $typelen = drupal_strlen($typekey);
  $systems = array();
  foreach ($form_state['storage'] as $key => $value) {
    if (drupal_substr($key, 0, $typelen) == $typekey) {
      if ($value == 1) {
        $system = drupal_substr($key, $typelen);
        $systems[$system] = 1;
      }
      unset($form_state['storage'][$key]);
      unset($form_state['values'][$key]);
    }
  }
  return $systems;
}
function coder_page() {
  $output = '<div class="coder-disclaimer">' . coder_help('coder#disclaimer', array()) . '</div>';
  $output .= drupal_get_form('coder_page_form');
  return $output;
}
function _coder_get_default_settings($arg = 'default') {
  $settings['coder_reviews'] = variable_get('coder_reviews', _coder_default_reviews());
  $settings['coder_severity'] = variable_get('coder_severity', SEVERITY_NORMAL);
  
  switch ($arg) {
    case 'settings':
      $settings['coder_includes'] = 1;
      break;
    case 'active':
      $settings['coder_active_modules'] = 1;
      break;
    case 'core':
      $settings['coder_core'] = 1;
      $settings['coder_includes'] = 1;
      break;
    case 'all':
      $settings['coder_core'] = 1;
      $settings['coder_includes'] = 1;
      $settings['coder_all'] = 1;
      break;
    case 'contrib':
      $settings['coder_includes'] = 1;
      $settings['coder_contrib'] = 1;
      break;
    case 'patches':
      $settings['coder_patches'] = 1;
      break;
    case 'default':
      $settings['coder_active_modules'] = variable_get('coder_active_modules', 1);
      $settings['coder_core'] = variable_get('coder_core', 0);
      $settings['coder_includes'] = variable_get('coder_includes', 0);
      $settings['coder_includes_exclusions'] = variable_get('coder_includes_exclusions', '');
      $settings['coder_modules'] = variable_get('coder_modules', array());
      $settings['coder_themes'] = variable_get('coder_themes', array());
      break;
    default:
      $settings['coder_includes'] = 1;
      $settings['coder_includes_exclusions'] = variable_get('coder_includes_exclusions', '');
      
      $settings['coder_modules'] = array(
        $arg => $arg,
      );
      break;
  }
  return $settings;
}
function coder_page_form_submit($form, &$form_state) {
  $form_state['storage'] = $form_state['values'];
}
function _coder_get_reviews_extensions($defaults, $reviews) {
  $extensions = array();
  foreach ($reviews as $review) {
    foreach ($review['#rules'] as $rule) {
      if (isset($rule['#filename'])) {
        foreach ($rule['#filename'] as $ext) {
          if (!in_array($ext, $defaults) && !in_array($ext, $extensions)) {
            $extensions[] = $ext;
          }
        }
      }
    }
  }
  return $extensions;
}
function coder_page_form($form_state) {
  if (isset($form_state['storage'])) {
    $settings = $form_state['storage'];
    $settings['coder_modules'] = _coder_settings_array($form_state, 'module');
    $settings['coder_themes'] = _coder_settings_array($form_state, 'theme');
    drupal_set_title(t('Code review (submitted options)'));
  }
  else {
    $arg = arg(1);
    $settings = _coder_get_default_settings($arg);
    if ($arg) {
      drupal_set_title(t('Code review (@options)', array(
        '@options' => isset($arg) ? $arg : 'default options',
      )));
    }
  }
  
  $reviews = array();
  $avail_reviews = _coder_reviews();
  $selected_reviews = $settings['coder_reviews'];
  foreach ($selected_reviews as $name => $checked) {
    if ($checked) {
      $reviews[$name] = $avail_reviews[$name];
    }
  }
  if ($coder_form = _coder_settings_form($settings, $system, $files)) {
    
    $path = drupal_get_path('module', 'coder');
    drupal_add_css($path . '/coder.css', 'module');
    
    $coder_includes_exclusions = isset($settings['coder_includes_exclusions']) ? $settings['coder_includes_exclusions'] : '';
    
    $ignores = coder_get_ignores();
    
    $module_weight = 0;
    if (isset($settings['coder_core']) && $settings['coder_core']) {
      $coder_args = array(
        '#reviews' => $reviews,
        '#severity' => $settings['coder_severity'],
        '#include_extensions' => array(),
      );
      $form['core_php'] = array(
        '#type' => 'fieldset',
        '#title' => 'core (php)',
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#weight' => ++$module_weight,
      );
      $phpfiles = file_scan_directory('.', '.*\\.php', array(
        '.',
        '..',
        'CVS',
        '.svn',
        '.git',
      ), 0, FALSE, 'name', 0);
      _coder_page_form_includes($form, $coder_args, 'core_php', $phpfiles, 2, $coder_includes_exclusions, $ignores);
      $form['core_includes'] = array(
        '#type' => 'fieldset',
        '#title' => 'core (includes)',
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#weight' => ++$module_weight,
      );
      $includefiles = drupal_system_listing('.*\\.inc$', 'includes', 'filename', 0);
      _coder_page_form_includes($form, $coder_args, 'core_includes', $includefiles, 0, $coder_includes_exclusions, $ignores);
    }
    
    if (isset($system)) {
      
      $php_extensions = variable_get('coder_php_ext', array(
        'inc',
        'php',
        'install',
        'test',
      ));
      $include_extensions = _coder_get_reviews_extensions($php_extensions, $reviews);
      $includes = array_merge($php_extensions, $include_extensions);
      
      $dups = array();
      $stats = array();
      $patch = '';
      foreach ($system as $name => $checked) {
        if ($checked) {
          
          $filename = $files[$name];
          if (!$filename) {
            drupal_set_message(t('Code Review file for %module not found', array(
              '%module' => $name,
            )), 'error');
            continue;
          }
          if (!empty($settings['coder_patches'])) {
            $patch = $filename;
            $filename = $name;
          }
          $coder_args = array(
            '#reviews' => $reviews,
            '#severity' => $settings['coder_severity'],
            '#filename' => $filename,
            '#patch' => $patch,
            '#php_extensions' => $php_extensions,
            '#include_extensions' => $include_extensions,
            '#ignore_lines' => isset($ignores[$filename]) && is_array($ignores[$filename]) ? $ignores[$filename] : array(),
            '#cache' => TRUE,
          );
          drupal_alter('coder_review_args', $coder_args);
          $results = do_coder_reviews($coder_args);
          $stats[$filename] = $results['#stats'];
          unset($results['#stats']);
          module_invoke_all('coder_results_view', $results);
          
          $form[$name] = array(
            '#type' => 'fieldset',
            '#title' => $filename,
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#weight' => ++$module_weight,
          );
          if (empty($results)) {
            $results[] = t('No Problems Found');
          }
          else {
            $form[$name]['#collapsed'] = FALSE;
          }
          $form[$name]['output'] = array(
            '#value' => theme('coder', $name, $filename, $results),
            '#weight' => -1,
          );
          
          if (!empty($settings['coder_includes'])) {
            
            if ($path = str_replace('\\', '/', dirname(realpath($filename)))) {
              if (!isset($dups[$path])) {
                if (drupal_substr($filename, -7) == '.module') {
                  $coder_args['#php_minor'] = 1;
                }
                $dups[$path] = 1;
                $regex = '\\.(' . implode('|', $includes) . ')$';
                $includefiles = drupal_system_listing($regex, dirname($filename), 'filename', 0);
                $offset = strpos($filename, dirname($filename));
                $stats[$filename]['#includes'] = _coder_page_form_includes($form, $coder_args, $name, $includefiles, $offset, $coder_includes_exclusions, $ignores);
              }
            }
          }
        }
      }
      if (count($stats)) {
        $summary = array(
          'files' => 0,
          'minor' => 0,
          'normal' => 0,
          'critical' => 0,
          'ignored' => 0,
        );
        foreach ($stats as $stat) {
          $summary['minor'] += $stat['minor'];
          $summary['normal'] += $stat['normal'];
          $summary['critical'] += $stat['critical'];
          $summary['ignored'] += $stat['ignored'];
          if (isset($stat['#includes'])) {
            foreach ($stat['#includes'] as $includestat) {
              $summary['files']++;
              $summary['minor'] += $includestat['minor'];
              $summary['normal'] += $includestat['normal'];
              $summary['critical'] += $includestat['critical'];
              $summary['ignored'] += $includestat['ignored'];
            }
          }
          $summary['files']++;
        }
        $display = array();
        if (empty($settings['coder_patches'])) {
          $display[] = t('Coder found @count projects', array(
            '@count' => count($stats),
          ));
          $display[] = t('@count files', array(
            '@count' => $summary['files'],
          ));
        }
        else {
          $display[] = t('Coder found @count patch hunks', array(
            '@count' => count($stats),
          ));
        }
        foreach (array(
          'critical',
          'normal',
          'minor',
        ) as $severity_name) {
          if ($summary[$severity_name] > 0) {
            $display[] = t('@count %severity_name warnings', array(
              '@count' => $summary[$severity_name],
              '%severity_name' => $severity_name,
            ));
          }
        }
        $display[] = t('@count warnings were flagged to be ignored', array(
          '@count' => $summary['ignored'],
        ));
        drupal_set_message(implode(', ', $display));
        if (function_exists('_coder_drush_is_option') && _coder_drush_is_option('drush')) {
          print coder_print_drush_messages();
        }
      }
    }
    
    $form['settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Selection form'),
      '#collapsible' => TRUE,
      '#collapsed' => isset($form),
      '#weight' => -1,
    );
    if ($form['settings']['#collapsed']) {
      $form['settings']['#prefix'] = t('<div class="coder-settings">Use the Selection form to select options for this code review, or change the <a href="@settings">Default Settings</a> and use the <a href="@default">Default</a> tab above.</div>', array(
        '@settings' => url('admin/settings/coder'),
        '@default' => url('coder/default'),
      ));
    }
    $form['settings'][] = $coder_form;
    $form['settings']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Submit'),
    );
  }
  return $form;
}
function _coder_page_form_includes(&$form, $coder_args, $name, $files, $offset, $exclusions, $ignores) {
  $stats = array();
  $coder_args['#name'] = $name;
  $weight = 0;
  $exclusions = str_replace(array(
    "\r\n",
    "\r",
    "\n",
    '/',
    '*',
    '.',
  ), array(
    '|',
    '|',
    '|',
    '\\/',
    '.*',
    '\\.',
  ), $exclusions);
  foreach ($files as $file) {
    if (!empty($exclusions) && preg_match("/{$exclusions}/", $file->filename)) {
      continue;
      
    }
    $filename = drupal_substr($file->filename, $offset);
    $php_extensions = variable_get('coder_review_php_ext', array(
      'inc',
      'php',
      'install',
      'test',
    ));
    $coder_args['#filename'] = $filename;
    $coder_args['#php_extensions'] = $php_extensions;
    $coder_args['#include_extensions'] = _coder_get_reviews_extensions($php_extensions, $coder_args['#reviews']);
    $coder_args['#ignore_lines'] = isset($ignores[$filename]) && is_array($ignores[$filename]) ? $ignores[$filename] : array();
    $results = do_coder_reviews($coder_args);
    $stats[$filename] = $results['#stats'];
    unset($results['#stats']);
    
    $form[$name][$filename] = array(
      '#type' => 'fieldset',
      '#title' => $filename,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#weight' => ++$weight,
    );
    if (empty($results)) {
      $results[] = t('No Problems Found');
    }
    else {
      $form[$name][$filename]['#collapsed'] = FALSE;
      $form[$name]['#collapsed'] = FALSE;
    }
    $form[$name][$filename]['output'] = array(
      '#value' => theme('coder', $name, $filename, $results),
    );
  }
  return $stats;
}
function _coder_modified() {
  static $_coder_mtime;
  if (!isset($_coder_mtime)) {
    $path = drupal_get_path('module', 'coder');
    $includefiles = drupal_system_listing('.*\\.(inc|module|coder_ignores.txt)$', $path . '/includes', 'filename', 0);
    $_coder_mtime = filemtime(realpath($path . '/coder.module'));
    foreach ($includefiles as $file) {
      $mtime = filemtime(realpath($file->filename));
      if ($mtime > $_coder_mtime) {
        $_coder_mtime = $mtime;
      }
    }
  }
  return $_coder_mtime;
}
function do_coder_reviews($coder_args) {
  if (empty($coder_args['#patch']) && empty($coder_args['#test']) && $coder_args['#cache']) {
    
    $cache_key = 'coder:' . md5(implode(':', array_keys($coder_args['#reviews']))) . $coder_args['#severity'] . ':' . $coder_args['#filename'];
    if (function_exists('_coder_drush_is_option') && _coder_drush_is_option('drush')) {
      if (_coder_drush_is_option('checkstyle')) {
        $cache_key .= ':drushcheckstyle';
      }
      elseif (_coder_drush_is_option('xml')) {
        $cache_key .= ':drushxml';
      }
    }
    if (file_exists($filepath = realpath($coder_args['#filename']))) {
      $cache_mtime = filemtime($filepath);
      if ($cache_results = cache_get($cache_key, 'cache_coder')) {
        if ($cache_results->data['mtime'] == $cache_mtime && _coder_modified() < $cache_results->created) {
          return $cache_results->data['results'];
        }
      }
    }
  }
  $results = array(
    '#stats' => array(
      'minor' => 0,
      'normal' => 0,
      'critical' => 0,
      'ignored' => 0,
    ),
  );
  
  if (isset($coder_args['#php_minor']) && drupal_substr($coder_args['#filename'], -4) == '.php') {
    if ($coder_args['#severity'] > 1) {
      return $results;
    }
  }
  
  if (_coder_read_and_parse_file($coder_args)) {
    
    $errors = array();
    foreach ($coder_args['#reviews'] as $review_name => $review) {
      $review['#review_name'] = $review_name;
      if ($result = do_coder_review($coder_args, $review)) {
        
        foreach (array(
          'critical',
          'normal',
          'minor',
          'ignored',
        ) as $severity_level) {
          if (isset($result['#stats'][$severity_level])) {
            $results['#stats'][$severity_level] += $result['#stats'][$severity_level];
          }
        }
        $errors += $result;
      }
    }
    
    foreach ($errors as $key => $error) {
      if (is_numeric($key)) {
        $results[$key] = theme('coder_warning_msg', $error);
      }
    }
    
    ksort($results, SORT_NUMERIC);
  }
  else {
    $results[] = theme('coder_warning', t('Could not read the file'), 'critical');
  }
  
  if (empty($coder_args['#patch']) && empty($coder_args['#test']) && $coder_args['#cache'] && isset($cache_mtime)) {
    $cache_results = array(
      'mtime' => $cache_mtime,
      'results' => $results,
    );
    cache_set($cache_key, $cache_results, 'cache_coder');
  }
  return $results;
}
function _coder_read_and_parse_file(&$coder_args) {
  $is_php_file = 1;
  $regex = '/(' . implode('|', array_merge(array(
    'module',
  ), $coder_args['#php_extensions'])) . ')$/';
  if (!preg_match($regex, $coder_args['#filename'])) {
    $is_php_file = 0;
    
  }
  
  if (!empty($coder_args['#patch']) || !empty($coder_args['#test']) || ($filepath = realpath($coder_args['#filename'])) && file_exists($filepath)) {
    $in_php = $is_php_file ? 0 : 1;
    $in_allphp = $is_php_file ? 0 : 1;
    $in_comment = 0;
    if (!empty($coder_args['#patch'])) {
      $content = $coder_args['#patch'];
      if (preg_match('/^\\s*\\*/', $content)) {
        $in_comment = '*';
      }
      else {
        $content = preg_replace('/^(function\\s.*?(\\r\\n|\\n)+)(\\s*\\*)/', '${1}/*', $content);
        $in_php = 1;
        $in_allphp = 1;
      }
    }
    elseif (!empty($coder_args['#test'])) {
      $content = $coder_args['#test'];
      $in_php = 1;
      $in_allphp = 1;
    }
    else {
      $content = file_get_contents($filepath) . "\n";
    }
    $content_length = drupal_strlen($content);
    $in_comment = 0;
    $beginning_of_line = 0;
    $in_quote_html = 0;
    $in_backslash = 0;
    $in_quote = 0;
    $in_heredoc = 0;
    $in_heredoc_html = '';
    $heredoc = '';
    $all_lines = array();
    $full_lines = array();
    $php_lines = array();
    $allphp_lines = array();
    $html_lines = array();
    $quote_lines = array();
    $doublequote_lines = array();
    $comment_lines = array();
    $this_all_lines = '';
    $this_php_lines = '';
    $this_allphp_lines = '';
    $this_html_lines = '';
    $this_quote_lines = array(
      '',
    );
    $this_quote_index = -1;
    $this_quote_sep = FALSE;
    $this_doublequote_lines = array(
      '',
    );
    $this_doublequote_index = -1;
    $this_comment_lines = '';
    
    $lineno = 1;
    for ($pos = 0; $pos < $content_length; $pos++) {
      
      $char = $content[$pos];
      if ($char == "\n") {
        if ($in_comment === '/') {
          
          $in_comment = 0;
        }
        
        $in_quote_html = 0;
        
        if ($this_all_lines != '') {
          if (trim($this_all_lines, "\r\n") != '') {
            $all_lines[$lineno] = array(
              $this_all_lines,
            );
            $full_lines[$lineno] = $this_all_lines;
          }
          if (trim($this_php_lines, "\r\n") != '') {
            $php_lines[$lineno] = array(
              $this_php_lines,
            );
          }
          if (trim($this_allphp_lines, "\r\n") != '') {
            $allphp_lines[$lineno] = array(
              $this_allphp_lines,
            );
          }
          if (trim($this_html_lines, "\r\n") != '') {
            $html_lines[$lineno] = array(
              $this_html_lines,
            );
          }
          $quotes = array();
          foreach ($this_quote_lines as $quote_line) {
            if (trim($quote_line, "\r\n") != '') {
              $quotes[] = $quote_line;
            }
          }
          if (count($quotes)) {
            $quote_lines[$lineno] = $quotes;
          }
          $quotes = array();
          foreach ($this_doublequote_lines as $quote_line) {
            if (trim($quote_line, "\r\n") != '') {
              $quotes[] = $quote_line;
            }
          }
          if (count($quotes)) {
            $doublequote_lines[$lineno] = $quotes;
          }
          if (trim($this_comment_lines, "\r\n") != '') {
            $comment_lines[$lineno] = array(
              $this_comment_lines,
            );
          }
        }
        
        $lineno++;
        $this_all_lines = '';
        $this_php_lines = '';
        $this_allphp_lines = '';
        $this_html_lines = '';
        $this_quote_lines = array(
          '',
        );
        $this_doublequote_lines = array(
          '',
        );
        $this_quote_index = -1;
        $this_quote_sep = FALSE;
        $this_doublequote_index = -1;
        $this_comment_lines = '';
        $beginning_of_line = 1;
        continue;
      }
      if ($this_all_lines != '') {
        $beginning_of_line = 0;
      }
      $this_all_lines .= $char;
      if ($in_php || $in_allphp) {
        
        if ($in_quote) {
          if ($in_backslash) {
            $in_backslash = 0;
          }
          elseif ($char == '\\') {
            $in_backslash = 1;
          }
          elseif ($char == $in_quote && !$in_backslash) {
            $in_quote = 0;
          }
          elseif ($char == '<') {
            $in_quote_html = '>';
          }
          if ($in_quote) {
            if ($this_quote_index == -1) {
              $this_quote_index = 0;
            }
            $this_quote_lines[$this_quote_index] .= $char;
            if ($in_quote == '"') {
              if ($this_doublequote_index == -1) {
                $this_doublequote_index = 0;
              }
              $this_doublequote_lines[$this_doublequote_index] .= $char;
            }
            if ($in_quote_html) {
              $this_html_lines .= $char;
            }
          }
          if ($char == $in_quote_html) {
            $in_quote_html = 0;
          }
          $this_allphp_lines .= $char;
          unset($char);
          
        }
        elseif ($in_heredoc) {
          if ($beginning_of_line && $char == $in_heredoc[0] && drupal_substr($content, $pos, $in_heredoc_length) == $in_heredoc) {
            $this_all_lines .= drupal_substr($content, $pos + 1, $in_heredoc_length - 1);
            $in_heredoc = 0;
            $pos += $in_heredoc_length;
          }
          elseif ($char == '<') {
            $in_heredoc_html = '>';
          }
          if ($in_heredoc && $in_heredoc_html) {
            $this_html_lines .= $char;
          }
          if ($in_heredoc_html && $char == $in_heredoc_html) {
            $in_heredoc_html = '';
          }
          unset($char);
        }
        elseif ($char == '?' && $content[$pos + 1] == '>' && $in_comment !== '*') {
          unset($char);
          $in_php = 0;
          $in_allphp = 0;
          $this_all_lines .= '>';
          $pos++;
        }
        elseif ($in_comment) {
          $this_comment_lines .= $char;
          if ($in_comment == '*' && $char == '*' && $content[$pos + 1] == '/') {
            $in_comment = 0;
            $this_all_lines .= '/';
            $this_comment_lines .= '/';
            $pos++;
          }
          unset($char);
          
        }
        else {
          switch ($char) {
            case ',':
            case ')':
            case '(':
              
              if ($this_quote_index < 0 || !empty($this_quote_lines[$this_quote_index])) {
                $this_quote_sep = TRUE;
              }
              break;
            case '\'':
            case '"':
              if ($content[$pos - 1] != '\\') {
                $this_php_lines .= $char;
                $in_quote = $char;
                if ($this_quote_sep) {
                  $this_quote_lines[++$this_quote_index] = '';
                  if ($char == '"') {
                    $this_doublequote_lines[++$this_doublequote_index] = '';
                  }
                }
                $this_quote_sep = FALSE;
              }
              break;
            case '/':
              $next_char = $content[$pos + 1];
              if ($next_char == '/' || $next_char == '*') {
                unset($char);
                $in_comment = $next_char;
                $this_all_lines .= $next_char;
                $this_comment_lines .= '/' . $next_char;
                $pos++;
              }
              break;
            case '<':
              if ($content[$pos + 1] == '<' && $content[$pos + 2] == '<') {
                unset($char);
                $this_all_lines .= '<<';
                
                for ($pos += 3; $pos < $content_length; $pos++) {
                  $char = $content[$pos];
                  if ($char == "\n") {
                    $pos--;
                    if (preg_match('/^\\s*(\\w+)/', $heredoc, $match)) {
                      $in_heredoc = $match[1];
                      $in_heredoc_length = drupal_strlen($in_heredoc);
                    }
                    break;
                  }
                  $this_all_lines .= $char;
                  $heredoc .= $char;
                }
                $heredoc = '';
                
                $this_php_lines .= '\'\'';
                $this_allphp_lines .= '\'\'';
                unset($char);
              }
              break;
          }
        }
        if (isset($char)) {
          $this_php_lines .= $char;
          $this_allphp_lines .= $char;
        }
      }
      else {
        switch ($char) {
          case '<':
            if ($content[$pos + 1] == '?') {
              if ($content[$pos + 2] == ' ') {
                $in_php = 1;
                $in_allphp = 1;
                $this_all_lines .= '? ';
                $pos += 2;
              }
              elseif (drupal_substr($content, $pos + 2, 3) == 'php') {
                $in_php = 1;
                $in_allphp = 1;
                $this_all_lines .= '?php';
                $pos += 4;
              }
              break;
            }
          
          default:
            $this_html_lines .= $char;
            break;
        }
      }
    }
    if (trim($this_all_lines) != '') {
      $all_lines[$lineno] = array(
        $this_all_lines,
      );
      $full_lines[$lineno] = $this_all_lines;
    }
    if (trim($this_php_lines) != '') {
      $php_lines[$lineno] = array(
        $this_php_lines,
      );
    }
    if (trim($this_allphp_lines) != '') {
      $allphp_lines[$lineno] = array(
        $this_allphp_lines,
      );
    }
    if (trim($this_html_lines) != '') {
      $html_lines[$lineno] = array(
        $this_html_lines,
      );
    }
    $quotes = array();
    foreach ($this_quote_lines as $quote_line) {
      if (trim($quote_line, "\r\n") != '') {
        $quotes[] = $quote_line;
      }
    }
    if (count($quotes)) {
      $quote_lines[$lineno] = $quotes;
    }
    $quotes = array();
    foreach ($this_doublequote_lines as $quote_line) {
      if (trim($quote_line, "\r\n") != '') {
        $quotes[] = $quote_line;
      }
    }
    if (count($quotes)) {
      $doublequote_lines[$lineno] = $quotes;
    }
    if (trim($this_comment_lines) != '') {
      $comment_lines[$lineno] = array(
        $this_comment_lines,
      );
    }
    
    $coder_args['#all_array_lines'] = $all_lines;
    $coder_args['#php_array_lines'] = $php_lines;
    $coder_args['#allphp_array_lines'] = $allphp_lines;
    $coder_args['#html_array_lines'] = $html_lines;
    $coder_args['#quote_array_lines'] = $quote_lines;
    $coder_args['#doublequote_array_lines'] = $doublequote_lines;
    $coder_args['#comment_array_lines'] = $comment_lines;
    $coder_args['#all_lines'] = $full_lines;
    return 1;
  }
}
function _coder_severity($severity_name, $default_value = SEVERITY_NORMAL) {
  
  if (!isset($severity_names)) {
    $severity_names = array(
      'minor' => SEVERITY_MINOR,
      'normal' => SEVERITY_NORMAL,
      'critical' => SEVERITY_CRITICAL,
    );
  }
  if (isset($severity_names[$severity_name])) {
    return $severity_names[$severity_name];
  }
  return $default_value;
}
function _coder_severity_name($coder_args, $review, $rule) {
  
  if (isset($coder_args['#php_minor']) && drupal_substr($coder_args['#filename'], -4) == '.php') {
    return 'minor';
  }
  
  if (isset($rule['#severity'])) {
    return $rule['#severity'];
  }
  
  if (isset($review['#severity'])) {
    return $review['#severity'];
  }
  
  return 'normal';
}
function do_coder_review($coder_args, $review) {
  $results = array(
    '#stats' => array(
      'minor' => 0,
      'normal' => 0,
      'critical' => 0,
      'ignored' => 0,
    ),
  );
  $allowed_extensions = array_merge($coder_args['#php_extensions'], $coder_args['#include_extensions'], array(
    'module',
  ));
  if ($review['#rules']) {
    
    $default_severity = isset($review['#severity']) ? _coder_severity($review['#severity']) : SEVERITY_NORMAL;
    
    $filename = empty($coder_args['#patch']) ? $coder_args['#filename'] : 'coder_review.patch';
    $is_patch = 0;
    
    if ($coder_args['#patch'] && preg_match('/(.+?):/', $coder_args['#filename'], $matches)) {
      $filename = empty($matches) ? 'coder_review.patch' : $matches[1];
      $is_patch = 1;
    }
    foreach ($review['#rules'] as $rule) {
      
      if ($filename_includes = isset($rule['#filename']) ? $rule['#filename'] : (isset($rule['#filename-not']) ? $coder_args['#php_extensions'] : NULL)) {
        $regex = '/(' . implode('|', $filename_includes) . ')$/i';
        if (!preg_match($regex, $filename, $matches)) {
          continue;
        }
      }
      
      $not = isset($rule['#filename-not']) ? $rule['#filename-not'] : (isset($rule['#filename']) ? NULL : $coder_args['#include_extensions']);
      if ($not) {
        $regex = '/(' . implode('|', $not) . ')$/i';
        
        if (preg_match($regex, $filename, $matches) || $is_patch && preg_match($regex, 'coder_review.patch', $matches)) {
          continue;
        }
      }
      
      if ($is_patch) {
        $regex = '/(' . implode('|', $allowed_extensions) . ')$/i';
        if (!preg_match($regex, $filename, $matches)) {
          continue;
        }
      }
      
      $severity = _coder_severity(isset($rule['#severity']) ? $rule['#severity'] : '', $default_severity);
      if ($severity >= $coder_args['#severity']) {
        if (isset($rule['#source'])) {
          
          $source = '#' . $rule['#source'] . '_array_lines';
          $lines = $coder_args[$source];
        }
        else {
          $lines = isset($coder_args['#php_array_lines']) ? $coder_args['#php_array_lines'] : array();
        }
        switch ($rule['#type']) {
          case 'regex':
            do_coder_review_regex($coder_args, $review, $rule, $lines, $results);
            break;
          case 'grep':
            do_coder_review_grep($coder_args, $review, $rule, $lines, $results);
            break;
          case 'grep_invert':
            do_coder_review_grep_invert($coder_args, $review, $rule, $lines, $results);
            break;
          case 'callback':
            do_coder_review_callback($coder_args, $review, $rule, $lines, $results);
            break;
        }
      }
    }
  }
  return $results;
}
function do_coder_review_regex(&$coder_args, $review, $rule, $lines, &$results) {
  if (isset($rule['#value']) && !empty($lines)) {
    $regexflags = isset($rule['#case-sensitive']) ? '' : 'i';
    $regex = '/' . $rule['#value'] . '/' . $regexflags;
    $class_regex = isset($rule['#class']) ? '/' . $rule['#class'] . '/' : '';
    $class_not_regex = isset($rule['#class-not']) ? '/' . $rule['#class-not'] . '/' : '';
    $class_current = '';
    $class_paren = 0;
    $function_regex = isset($rule['#function']) ? '/' . $rule['#function'] . '/' : '';
    $function_not_regex = isset($rule['#function-not']) ? '/' . $rule['#function-not'] . '/' : '';
    $function_current = '';
    $function_paren = 0;
    $not_regex = isset($rule['#not']) ? '/' . $rule['#not'] . '/' . $regexflags : '';
    $never_regex = isset($rule['#never']) ? '/' . $rule['#never'] . '/' . $regexflags : '';
    foreach ($lines as $lineno => $line_array) {
      foreach ($line_array as $line) {
        
        if ($function_regex || $function_not_regex) {
          if (preg_match('/function (\\w+)\\(/', $line, $match)) {
            $function_current = $match[1];
          }
          if (preg_match('/([{}])/', $line, $match)) {
            $function_paren += $match[0] == '{' ? 1 : -1;
          }
          if ($function_paren < 0 || $function_current == '' || $function_regex && !preg_match($function_regex, $function_current) || $function_not_regex && preg_match($function_not_regex, $function_current)) {
            continue;
          }
        }
        
        if ($class_regex || $class_not_regex) {
          if (preg_match('/class (\\w+)/', $line, $match) || preg_match('/interface (\\w+)/', $line, $match)) {
            $class_current = $match[1];
          }
          if (preg_match('/([{}])/', $line, $match)) {
            $class_paren += $match[0] == '{' ? 1 : -1;
          }
          if ($class_paren < 0 || $class_regex && $class_current == '' || $class_regex && !preg_match($class_regex, $class_current) || $class_not_regex && preg_match($class_not_regex, $class_current)) {
            continue;
          }
        }
        if (preg_match($regex, $line, $matches)) {
          
          if ($not_regex) {
            foreach ($matches as $match) {
              if (preg_match($not_regex, $match)) {
                continue 2;
              }
            }
          }
          if ($never_regex) {
            if (preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
              continue;
            }
          }
          $line = $coder_args['#all_lines'][$lineno];
          $severity_name = _coder_severity_name($coder_args, $review, $rule);
          _coder_error($results, $rule, $severity_name, $lineno, $line, $coder_args['#ignore_lines'][$review['#review_name']]);
        }
      }
    }
  }
}
function _coder_error(&$results, $rule, $severity_name, $lineno = -1, $line = '', $ignores = array()) {
  
  global $_coder_errno;
  
  if (is_array($ignores) && in_array($lineno, $ignores)) {
    $results['#stats']['ignored']++;
  }
  else {
    $key = ($lineno + 1) * 10000 + $_coder_errno++;
    $results[$key] = array(
      'rule' => $rule,
      'severity_name' => $severity_name,
      'lineno' => $lineno,
      'line' => $line,
    );
    $results['#stats'][$severity_name]++;
  }
}
function do_coder_review_grep(&$coder_args, $review, $rule, $lines, &$results) {
  if (isset($rule['#value'])) {
    foreach ($lines as $lineno => $line_array) {
      foreach ($line_array as $line) {
        if (_coder_search_string($line, $rule)) {
          $line = $coder_args['#all_lines'][$lineno];
          $severity_name = _coder_severity_name($coder_args, $review, $rule);
          _coder_error($results, $rule, $severity_name, $lineno, $line, $coder_args['#ignore_lines'][$review['#review_name']]);
        }
      }
    }
  }
}
function do_coder_review_grep_invert(&$coder_args, $review, $rule, $lines, &$results) {
  if (isset($rule['#value'])) {
    foreach ($lines as $lineno => $line_array) {
      foreach ($line_array as $line) {
        if (_coder_search_string($line, $rule)) {
          return;
        }
      }
    }
    $severity_name = _coder_severity_name($coder_args, $review, $rule);
    _coder_error($results, $rule, $severity_name);
  }
}
function do_coder_review_callback(&$coder_args, $review, $rule, $lines, &$results) {
  if ($function = $rule['#value']) {
    if (function_exists($function)) {
      call_user_func_array($function, array(
        &$coder_args,
        $review,
        $rule,
        $lines,
        &$results,
      ));
    }
  }
}
function _coder_search_string($line, $rule) {
  static $php5;
  if (!isset($php5)) {
    if (function_exists('stripos')) {
      $php5 = TRUE;
    }
    else {
      $php5 = FALSE;
    }
  }
  
  if (isset($rule['#case-sensitive'])) {
    return strpos($line, $rule['#value']) !== FALSE;
  }
  
  if ($php5 && !isset($rule['#case-sensitive'])) {
    return stripos($line, $rule['#value']) !== FALSE;
  }
  
  $regex = '/' . preg_quote($rule['#value']) . '/i';
  return preg_match($regex, $line);
}
function _coder_is_drupal_core($module) {
  static $core;
  if (!isset($core)) {
    $core = array(
      
      'aggregator' => 1,
      'block' => 1,
      'blog' => 1,
      'blogapi' => 1,
      'book' => 1,
      'color' => 1,
      'comment' => 1,
      'contact' => 1,
      'dblog' => 1,
      'filter' => 1,
      'forum' => 1,
      'help' => 1,
      'locale' => 1,
      'menu' => 1,
      'node' => 1,
      'openid' => 1,
      'path' => 1,
      'php' => 1,
      'ping' => 1,
      'poll' => 1,
      'profile' => 1,
      'search' => 1,
      'statistics' => 1,
      'syslog' => 1,
      'system' => 1,
      'taxonomy' => 1,
      'throttle' => 1,
      'tracker' => 1,
      'translation' => 1,
      'trigger' => 1,
      'update' => 1,
      'upload' => 1,
      'user' => 1,
      
      'bluemarine' => 1,
      'chameleon' => 1,
      'garland' => 1,
      'marvin' => 1,
      'minnelli' => 1,
      'pushbutton' => 1,
    );
  }
  return isset($core[$module->name]) ? 1 : 0;
}
function coder_get_ignores() {
  $ignores = coder_parse_ignores(drupal_get_path('module', 'coder') . '/core.coder_ignores.txt');
  foreach (module_implements('coder_ignore') as $module) {
    $function = $module . '_coder_ignore';
    $default_info = array(
      'path' => '',
      'line prefix' => '',
    );
    $info = array_merge($default_info, $function());
    $ignores += coder_parse_ignores($info['path'] . '/' . $module . '.coder_ignores.txt', $info['line prefix']);
  }
  return $ignores;
}
function coder_parse_ignores($filepath, $line_prefix = '') {
  $ignores = array();
  
  if (!is_file($filepath) || !is_readable($filepath)) {
    drupal_set_message(t("File %file doesn't exist or isn't readable.", array(
      '%file' => $filepath,
    )), 'error');
    return array();
  }
  
  $lines = file($filepath);
  
  foreach ($lines as $line) {
    $line = trim($line);
    if (empty($line) || preg_match('/^;/', $line, $matches)) {
      continue;
    }
    $line = $line_prefix . $line;
    
    $parts = explode(':', $line);
    
    $ignores[$parts[0]][$parts[2]][] = $parts[1];
  }
  return $ignores;
}
function coder_simpletest() {
  return array_keys(file_scan_directory(drupal_get_path('module', 'coder') . '/tests', '\\.test'));
}
function coder_theme() {
  return array(
    'coder' => array(
      'arguments' => array(
        'name',
        'filename',
        'results',
      ),
    ),
    'coder_warning' => array(
      'arguments' => array(
        'warning',
        'severity_name',
        'lineno',
        'line',
      ),
    ),
    'coder_warning_msg' => array(
      'arguments' => array(
        'error',
      ),
    ),
    'cols' => array(
      'arguments' => array(
        'form',
      ),
    ),
    'drupalapi' => array(
      'arguments' => array(
        'function',
        'version',
      ),
    ),
    'phpapi' => array(
      'arguments' => array(
        'function',
      ),
    ),
  );
}
function theme_coder($name, $filename, $results) {
  
  if (function_exists('_coder_drush_is_option') && _coder_drush_is_option('drush')) {
    return theme_drush_coder($name, $filename, $results);
  }
  $output = '<div class="coder"><h2>' . basename($filename) . '</h2>';
  if (!empty($results)) {
    $output .= theme('item_list', $results);
  }
  $output .= '</div>';
  return $output;
}
function theme_coder_warning_msg($error) {
  
  return theme('coder_warning', _coder_warning($error['rule']), $error['severity_name'], $error['lineno'], $error['line']);
}
function _coder_warning($rule) {
  if (isset($rule['#warning_callback'])) {
    if (function_exists($rule['#warning_callback'])) {
      return $rule['#warning_callback']();
    }
    return t('please <a href="@report">report</a> this !warning', array(
      '@report' => 'http://drupal.org/node/add/project_issue/coder/bug',
      '!warning' => $rule['#warning_callback'],
    ));
  }
  return t($rule['#warning']);
}
function theme_coder_warning($warning, $severity_name, $lineno = 0, $line = '') {
  
  if (function_exists('_coder_drush_is_option') && _coder_drush_is_option('drush')) {
    return theme_drush_coder_warning($warning, $severity_name, $lineno, $line);
  }
  
  if (is_array($warning)) {
    $description = isset($warning['#description']) ? $warning['#description'] : '';
    $link = $warning['#link'];
    $warning_msg = $warning['#warning'];
    if (isset($warning['#link'])) {
      $warning_msg .= '  (' . l(t('Drupal Docs'), $warning['#link']) . ')';
    }
    $warning = $warning_msg;
  }
  if ($lineno) {
    $warning = t('Line @number: !warning', array(
      '@number' => $lineno,
      '!warning' => $warning,
    ));
    if ($line) {
      $warning .= '<pre>' . check_plain($line) . '</pre>';
    }
  }
  $class = 'coder-warning';
  if ($severity_name) {
    $class .= " coder-{$severity_name}";
  }
  $path = drupal_get_path('module', 'coder');
  $title = t('severity: @severity', array(
    '@severity' => $severity_name,
  ));
  $img = theme('image', $path . "/images/{$severity_name}.png", $title, $title, array(
    'align' => 'right',
    'class' => 'coder',
  ), FALSE);
  if (!empty($description)) {
    $img .= theme('image', $path . '/images/more.png', t('click to read more'), '', array(
      'align' => 'right',
      'class' => 'coder-more',
    ), FALSE);
    $warning .= '<div class="coder-description">Explanation: ' . $description . '</div>';
  }
  return '<div class="' . $class . '">' . $img . $warning . '</div>';
}
function theme_drupalapi($function, $version = '') {
  return l($function, "http://api.drupal.org/api/function/{$function}/{$version}");
}
function theme_phpapi($function) {
  return l($function, "http://php.net/{$function}");
}