Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.446
diff -u -p -r1.446 form.inc
--- includes/form.inc	30 Mar 2010 07:05:58 -0000	1.446
+++ includes/form.inc	30 Mar 2010 18:06:14 -0000
@@ -538,6 +538,7 @@ function drupal_retrieve_form($form_id, 
     }
     if (isset($form_definition['callback'])) {
       $callback = $form_definition['callback'];
+      $form_state['build_info']['base_form_id'] = $callback;
     }
     // In case $form_state['wrapper_callback'] is not defined already, we also
     // allow hook_forms() to define one.
@@ -737,21 +738,37 @@ function drupal_prepare_form($form_id, &
   $form += array('#tree' => FALSE, '#parents' => array());
 
   if (!isset($form['#validate'])) {
+    // Check for a handler specific to $form_id.
     if (function_exists($form_id . '_validate')) {
-      $form['#validate'] = array($form_id . '_validate');
+      $form['#validate'][] = $form_id . '_validate';
+    }
+    // Otherwise, check whether this is a shared form and whether there is a
+    // handler for the shared $form_id.
+    elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) {
+      $form['#validate'][] = $form_state['build_info']['base_form_id'] . '_validate';
     }
   }
 
   if (!isset($form['#submit'])) {
+    // Check for a handler specific to $form_id.
     if (function_exists($form_id . '_submit')) {
-      // We set submit here so that it can be altered.
-      $form['#submit'] = array($form_id . '_submit');
+      $form['#submit'][] = $form_id . '_submit';
+    }
+    // Otherwise, check whether this is a shared form and whether there is a
+    // handler for the shared $form_id.
+    elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
+      $form['#submit'][] = $form_state['build_info']['base_form_id'] . '_submit';
     }
   }
 
   // Invoke hook_form_FORM_ID_alter() implementations.
   drupal_alter('form_' . $form_id, $form, $form_state);
 
+  // Invoke hook_form_BASE_FORM_ID_alter() implementations.
+  if (isset($form_state['build_info']['base_form_id'])) {
+    drupal_alter('form_' . $form_state['build_info']['base_form_id'], $form, $form_state);
+  }
+
   // Invoke hook_form_alter() implementations.
   drupal_alter('form', $form, $form_state, $form_id);
 }
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.858
diff -u -p -r1.858 comment.module
--- modules/comment/comment.module	28 Mar 2010 11:08:30 -0000	1.858
+++ modules/comment/comment.module	30 Mar 2010 17:55:17 -0000
@@ -677,7 +677,7 @@ function comment_node_view($node, $view_
     }
 
     $node->content['links']['comment'] = array(
-      '#theme' => 'links__comment_node',
+      '#theme' => 'links__comment_node__' . $node->type,
       '#links' => $links,
       '#attributes' => array('class' => array('links', 'inline')),
     );
@@ -686,7 +686,7 @@ function comment_node_view($node, $view_
     // page. We compare $node and $page_node to ensure that comments are not
     // appended to other nodes shown on the page, for example a node_reference
     // displayed in 'full' view mode within another node.
-    if ($node->comment && node_is_page($node) && empty($node->in_preview) && user_access('access comments')) {
+    if ($node->comment && node_is_page($node) && empty($node->in_preview)) {
       $node->content['comments'] = comment_node_page_additions($node);
     }
   }
@@ -704,7 +704,7 @@ function comment_node_page_additions($no
   // Only attempt to render comments if the node has visible comments.
   // Unpublished comments are not included in $node->comment_count, so show
   // comments unconditionally if the user is an administrator.
-  if ($node->comment_count || user_access('administer comments')) {
+  if (($node->comment_count && user_access('access comments')) || user_access('administer comments')) {
     $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
     $comments_per_page = variable_get('comment_default_per_page_' . $node->type, 50);
     if ($cids = comment_get_thread($node, $mode, $comments_per_page)) {
@@ -719,13 +719,13 @@ function comment_node_page_additions($no
 
   // Append comment form if needed.
   if (user_access('post comments') && $node->comment == COMMENT_NODE_OPEN && (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW)) {
-    $build = drupal_get_form('comment_form', (object) array('nid' => $node->nid));
+    $build = drupal_get_form($node->type . '_comment_form', (object) array('nid' => $node->nid));
     $additions['comment_form'] = $build;
   }
 
   if ($additions) {
     $additions += array(
-      '#theme' => 'comment_wrapper',
+      '#theme' => 'comment_wrapper__' . $node->type,
       '#node' => $node,
       'comments' => array(),
       'comment_form' => array(),
@@ -902,7 +902,7 @@ function comment_view($comment, $node, $
   unset($comment->content);
 
   $build += array(
-    '#theme' => 'comment',
+    '#theme' => 'comment__' . $node->type,
     '#comment' => $comment,
     '#node' => $node,
     '#view_mode' => $view_mode,
@@ -1706,7 +1706,21 @@ function comment_get_display_page($cid, 
  */
 function comment_edit_page($comment) {
   drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject)), PASS_THROUGH);
-  return drupal_get_form('comment_form', $comment);
+  $node = node_load($comment->nid);
+  return drupal_get_form($node->type . '_comment_form', $comment);
+}
+
+/**
+ * Implements hook_forms().
+ */
+function comment_forms() {
+  $forms = array();
+  if ($types = node_type_get_types()) {
+    foreach (array_keys($types) as $type) {
+      $forms[$type . '_comment_form']['callback'] = 'comment_form';
+    }
+  }
+  return $forms;
 }
 
 /**
@@ -1723,6 +1737,8 @@ function comment_form($form, &$form_stat
   $node = node_load($comment->nid);
   $form['#node'] = $node;
 
+  $form['#attributes']['class'][] = 'comment-form';
+
   $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
   $is_admin = (!empty($comment->cid) && user_access('administer comments'));
 
Index: modules/comment/comment.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.pages.inc,v
retrieving revision 1.37
diff -u -p -r1.37 comment.pages.inc
--- modules/comment/comment.pages.inc	28 Mar 2010 07:00:30 -0000	1.37
+++ modules/comment/comment.pages.inc	30 Mar 2010 17:55:17 -0000
@@ -37,7 +37,7 @@ function comment_reply($node, $pid = NUL
     // The user is previewing a comment prior to submitting it.
     if ($op == t('Preview')) {
       if (user_access('post comments')) {
-        $build['comment_form'] = drupal_get_form('comment_form', (object) array('pid' => $pid, 'nid' => $node->nid));
+        $build['comment_form'] = drupal_get_form($node->type . '_comment_form', (object) array('pid' => $pid, 'nid' => $node->nid));
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
@@ -84,7 +84,7 @@ function comment_reply($node, $pid = NUL
       }
       elseif (user_access('post comments')) {
         $edit = array('nid' => $node->nid, 'pid' => $pid);
-        $build['comment_form'] = drupal_get_form('comment_form', (object) $edit);
+        $build['comment_form'] = drupal_get_form($node->type . '_comment_form', (object) $edit);
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.290
diff -u -p -r1.290 locale.module
--- modules/locale/locale.module	30 Mar 2010 07:17:19 -0000	1.290
+++ modules/locale/locale.module	30 Mar 2010 17:55:17 -0000
@@ -974,7 +974,7 @@ function locale_url_outbound_alter(&$pat
   }
 }
 
-/*
+/**
  * Implements hook_form_FORM_ID_alter().
  */
 function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.148
diff -u -p -r1.148 system.api.php
--- modules/system/system.api.php	27 Mar 2010 18:41:14 -0000	1.148
+++ modules/system/system.api.php	30 Mar 2010 18:17:21 -0000
@@ -796,6 +796,40 @@ function hook_form_FORM_ID_alter(&$form,
 }
 
 /**
+ * Provide a form-specific alteration for shared forms.
+ *
+ * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
+ * form belonging to multiple form_ids, rather than implementing
+ * hook_form_alter() and checking for conditions that would identify the
+ * shared form constructor.
+ *
+ * Examples for such forms are node_form() or comment_form().
+ *
+ * Note that this hook fires after hook_form_FORM_ID_alter() and before
+ * hook_form_alter().
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form.
+ *
+ * @see hook_form_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ */
+function hook_form_BASE_FORM_ID_alter(&$form, &$form_state) {
+  // Modification for the form with the given BASE_FORM_ID goes here. For
+  // example, if BASE_FORM_ID is "node_form", this code would run on every
+  // node form, regardless of node type.
+
+  // Add a checkbox to the node form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
  * Map form_ids to form builder functions.
  *
  * By default, when drupal_get_form() is called, the system will look for a
