diff -urp includes/menu.inc includes/menu.inc
--- includes/menu.inc	2008-12-03 22:03:08.000000000 +0200
+++ includes/menu.inc	2008-12-03 21:58:32.000000000 +0200
@@ -488,22 +488,33 @@ function _menu_load_objects(&$item, &$ma
  *   $item['access'] becomes TRUE if the item is accessible, FALSE otherwise.
  */
 function _menu_check_access(&$item, $map) {
-  // Determine access callback, which will decide whether or not the current
-  // user has access to this path.
-  $callback = empty($item['access_callback']) ? 0 : trim($item['access_callback']);
-  // Check for a TRUE or FALSE value.
-  if (is_numeric($callback)) {
-    $item['access'] = (bool)$callback;
-  }
-  else {
-    $arguments = menu_unserialize($item['access_arguments'], $map);
-    // As call_user_func_array is quite slow and user_access is a very common
-    // callback, it is worth making a special case for it.
-    if ($callback == 'user_access') {
-      $item['access'] = (count($arguments) == 1) ? user_access($arguments[0]) : user_access($arguments[0], $arguments[1]);
+  $callbacks = array();
+  foreach (unserialize($item['access_arguments']) as $callback) {
+    $callbacks[] = array(
+      'callback' => empty($callback['callback']) ? 0 : trim($callback['callback']),
+       'arguments' => $callback['arguments'],
+     );
+   }
+   foreach ($callbacks as $callback) {
+    // Check for a TRUE or FALSE value.
+    if (is_numeric($callback['callback'])) {
+      $item['access'] =  (bool)$callback['callback'];
     }
     else {
-      $item['access'] = call_user_func_array($callback, $arguments);
+      $arguments = menu_unserialize($callback['arguments'], $map);
+      // As call_user_func_array is quite slow and user_access is a very common
+      // callback, it is worth making a special case for it.
+      if ($callback['callback'] == 'user_access') {
+        $item['access'] = count($arguments) == 1 ? user_access($arguments[0]) : user_access($arguments[0], $arguments[1]);
+      }
+      else {
+        $item['access'] = call_user_func_array($callback['callback'], $arguments);
+      }
+    }
+    // If menu has multiple access and one access is FALSE then deny access
+    // and stop checking other access callbacks.
+    if ($item['access'] == FALSE) {
+      break;
     }
   }
 }
@@ -545,9 +556,6 @@ function _menu_item_localize(&$item, $ma
       if (empty($item['title_arguments'])) {
         $item['title'] = t($item['title']);
       }
-      else {
-        $item['title'] = t($item['title'], menu_unserialize($item['title_arguments'], $map));
-      }
     }
     elseif (drupal_function_exists($callback)) {
       if (empty($item['title_arguments'])) {
@@ -2405,7 +2412,32 @@ function _menu_router_build($callbacks) 
   // First pass: separate callbacks from paths, making paths ready for
   // matching. Calculate fitness, and fill some default values.
   $menu = array();
-  foreach ($callbacks as $path => $item) {
+  // We pass $item by reference, as we will populate it with the
+  // grouped callbacks.
+  foreach ($callbacks as $path => &$item) {
+    /******************************************************/
+    // Temporary, just until core's hook_menu() are updated.
+    $item += array(
+      'access' => array(
+        array(
+          'callback' => isset($item['access callback']) ? $item['access callback'] : NULL,
+          'arguments' => isset($item['access arguments']) ? $item['access arguments'] : NULL,
+        ),
+      ),
+      'page' => array(
+        'callback' => isset($item['page callback']) ? $item['page callback'] : NULL,
+        'arguments' => isset($item['page arguments']) ? $item['page arguments'] : NULL,
+      ),
+      'block' => array(
+        'callback' => isset($item['block callback']) ? $item['block callback'] : NULL,
+      ),
+      'load' => array(
+        'arguments' => isset($item['load arguments']) ? $item['load arguments'] : NULL,
+      ),
+    );
+
+    /******************************************************/
+
     $load_functions = array();
     $to_arg_functions = array();
     $fit = 0;
@@ -2438,7 +2470,7 @@ function _menu_router_build($callbacks) 
             // Create an array of arguments that will be passed to the _load
             // function when this menu path is checked, if 'load arguments'
             // exists.
-            $load_functions[$k] = isset($item['load arguments']) ? array($function => $item['load arguments']) : $function;
+            $load_functions[$k] = isset($item['load']['arguments']) ? array($function => $item['load']['arguments']) : $function;
             $match = TRUE;
           }
         }
@@ -2483,7 +2515,6 @@ function _menu_router_build($callbacks) 
     }
   }
   array_multisort($sort, SORT_NUMERIC, $menu);
-
   if (!$menu) {
     return array();
   }
@@ -2524,9 +2555,8 @@ function _menu_router_build($callbacks) 
     for ($i = $item['_number_parts'] - 1; $i; $i--) {
       $parent_path = implode('/', array_slice($item['_parts'], 0, $i));
       if (isset($menu[$parent_path])) {
-
+        // Get the parent item if inheritance rules should be applied.
         $parent = $menu[$parent_path];
-
         if (!isset($item['tab_parent'])) {
           // Parent stores the parent of the path.
           $item['tab_parent'] = $parent_path;
@@ -2534,43 +2564,103 @@ function _menu_router_build($callbacks) 
         if (!isset($item['tab_root']) && !$parent['_tab']) {
           $item['tab_root'] = $parent_path;
         }
+        // A parent was found so we can break.
+        break;
+      }
+    }
+
+    // Process the access callbacks, which might have multiple callbacks.
+    if (!empty($item['access'])) {
+      // Pass by reference as we might change the callback itself.
+      foreach ($item['access'] as $key => &$callback) {
+
         // If an access callback is not found for a default local task we use
         // the callback from the parent, since we expect them to be identical.
         // In all other cases, the access parameters must be specified.
-        if (($item['type'] == MENU_DEFAULT_LOCAL_TASK) && !isset($item['access callback']) && isset($parent['access callback'])) {
-          $item['access callback'] = $parent['access callback'];
-          if (!isset($item['access arguments']) && isset($parent['access arguments'])) {
-            $item['access arguments'] = $parent['access arguments'];
+        if (isset($callback['callback'])) {
+          if (is_bool($callback['callback'])) {
+            $callback['callback'] = intval($callback['callback']);
           }
         }
-        // Same for page callbacks.
-        if (!isset($item['page callback']) && isset($parent['page callback'])) {
-          $item['page callback'] = $parent['page callback'];
-          if (!isset($item['page arguments']) && isset($parent['page arguments'])) {
-            $item['page arguments'] = $parent['page arguments'];
-          }
+        elseif ($item['type'] == MENU_DEFAULT_LOCAL_TASK && isset($parent['access'][$key]['callback'])) {
+          // Set to parent access callback.
+          // We assume the parent's access callback is in the same
+          // callback location as the item.
+          $callback['callback'] = $parent['access'][$key]['callback'];
+          // If also the arguments are missing set the parent's
+          // arguments.
+          $callback['arguments'] = isset($callback['arguments']) ? $callback['arguments'] : $parent['access'][$key]['arguments'];
+        }
+        elseif (isset($callback['arguments'])) {
+          // Default access callback.
+          $callback['callback'] = 'user_access';
+        }
+        // Serialize access arguments as it will later be menu_unserialize().
+        if (isset($callback['arguments'])) {
+          $callback['arguments'] = is_array($callback['arguments']) ? serialize($callback['arguments']) : $callback['arguments'];
+        }
+        else {
+          $callback['arguments'] = serialize(array());
         }
       }
     }
-    if (!isset($item['access callback']) && isset($item['access arguments'])) {
-      // Default callback.
-      $item['access callback'] = 'user_access';
+
+    // Set parent inheritance for page callbacks.
+    if (!isset($item['page']['callback']) && isset($parent['page']['callback'])) {
+      $item['page']['callback'] = $parent['page']['callback'];
+      if (!isset($item['page']['arguments'])) {
+        $item['page']['arguments'] = isset($parent['page']['arguments']) ? $parent['page']['arguments'] : array();
+      }
     }
-    if (!isset($item['access callback']) || empty($item['page callback'])) {
-      $item['access callback'] = 0;
+
+    // Serialize page arguments as it will later be menu_unserialize().
+    if (isset($item['page']['arguments'])) {
+      $item['page']['arguments'] = is_array($item['page']['arguments']) ? serialize($item['page']['arguments']) : $item['page']['arguments'];
     }
-    if (is_bool($item['access callback'])) {
-      $item['access callback'] = intval($item['access callback']);
+    else {
+      $item['page']['arguments'] = serialize(array());
     }
 
+    // Assign block default values.
+    $item['block']['callback'] = isset($item['block']['callback']) ? $item['block']['callback'] : '';
+
+    // Assign load default values.
+    $item['load']['arguments'] = isset($item['load']['arguments']) ? $item['load']['arguments'] : array();
+
+    // For API simplification we allow defining 'title' => 'foo' where t() is
+    // the default callback.
+
+    if (empty($item['title'])) {
+      // We will need to revert the title back to this value.
+      $original_title = isset($item['title']['arguments']) ? $item['title']['arguments'] : '';
+
+      $item['title'] = array(
+        'callback' => isset($item['title']['callback']) ? $item['title']['callback'] : '',
+        'arguments' => isset($item['title']['arguments']) ? $item['title']['arguments'] : '',
+      );
+    }
+    else {
+      $original_title = $item['title'];
+      $item['title'] = array(
+        'callback' => 't',
+        'arguments' => array($item['title']),
+      );
+    }
+
+    // Make sure there is atleast a single default value assigned to
+    // access callback.
+    if (empty($item['access']) || empty($item['page'])) {
+      $item['access'][0]['callback'] = 0;
+    }
+    // Set parent inheritance for file and filepath.
+    if (!isset($item['file']) && isset($parent['file'])) {
+      $item['file'] = $parent['file'];
+    }
+    if (!isset($item['file path']) && isset($parent['file path'])) {
+      $item['file path'] = $parent['file path'];
+    }
+    // Assign default values.
     $item += array(
-      'access arguments' => array(),
-      'access callback' => '',
-      'page arguments' => array(),
-      'page callback' => '',
-      'block callback' => '',
-      'title arguments' => array(),
-      'title callback' => 't',
       'description' => '',
       'position' => '',
       'tab_parent' => '',
@@ -2583,23 +2673,26 @@ function _menu_router_build($callbacks) 
       'path' => $item['path'],
       'load_functions' => $item['load_functions'],
       'to_arg_functions' => $item['to_arg_functions'],
-      'access_callback' => $item['access callback'],
-      'access_arguments' => serialize($item['access arguments']),
-      'page_callback' => $item['page callback'],
-      'page_arguments' => serialize($item['page arguments']),
+      'access_callback' => '',
+      'access_arguments' => serialize($item['access']),
+      'page_callback' => $item['page']['callback'],
+      'page_arguments' => $item['page']['arguments'],
       'fit' => $item['_fit'],
       'number_parts' => $item['_number_parts'],
       'tab_parent' => $item['tab_parent'],
       'tab_root' => $item['tab_root'],
-      'title' => $item['title'],
-      'title_callback' => $item['title callback'],
-      'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
+      'title' => '',
+      'title_callback' => $item['title']['callback'],
+      'title_arguments' => serialize($item['title']['arguments']),
       'type' => $item['type'],
-      'block_callback' => $item['block callback'],
+      'block_callback' => $item['block']['callback'],
       'description' => $item['description'],
       'position' => $item['position'],
       'weight' => $item['weight'],
     ));
+
+    // Reset title back as it will be needed later.
+    $item['title'] = $original_title;
   }
   // Execute insert object.
   $insert->execute();
