=== added file 'includes/archiver.inc'
--- includes/archiver.inc	1970-01-01 00:00:00 +0000
+++ includes/archiver.inc	2009-10-15 07:08:32 +0000
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Shared classes and interfaces for the archiver system.
+ */
+
+/**
+ * Common interface for all Archiver classes.
+ */
+interface ArchiverInterface {
+
+  /**
+   * Contructor for a new archiver instance.
+   *
+   * @param $file_path
+   *   The full system path of the archive to manipulate.  Only local files
+   *   are supported.  If the file does not yet exist, it will be created if
+   *   appropriate.
+   */
+  public function __construct($file_path);
+
+  /**
+   * Add the specified file or directory to the archive.
+   *
+   * @param $file_path
+   *   The full system path of the file or directory to add. Only local files
+   *   and directories are supported.
+   * @return
+   *   The called object.
+   */
+  public function add($file_path);
+
+  /**
+   * Remove the specified file from the archive.
+   *
+   * @param $path
+   *   The file name relative to the root of the archive to remove.
+   * @return
+   *   The called object.
+   */
+  public function remove($path);
+
+  /**
+   * Extract multiple files in the archive to the specified path.
+   *
+   * @param $path
+   *   A full system path of the directory to which to extract files.
+   * @param $files
+   *   Optionally specify a list of files to be extracted. Files are
+   *   relative to the root of the archive. If not specified, all files
+   *   in the archive will be extracted
+   * @return
+   *   The called object.
+   */
+  public function extract($path, Array $files = array());
+
+  /**
+   * List all files in the archive.
+   *
+   * @return
+   *   An array of file names relative to the root of the archive, or
+   *   an iteratable object that resolves to such a list.
+   */
+  public function listContents();
+}
+

=== modified file 'includes/common.inc'
--- includes/common.inc	2009-10-13 21:16:42 +0000
+++ includes/common.inc	2009-10-15 08:06:51 +0000
@@ -5797,3 +5797,53 @@ function xmlrpc($url) {
   return call_user_func_array('_xmlrpc', $args);
 }
 
+/**
+ * Retrieve a list of all available archivers.
+ */
+function archiver_get_info() {
+  $archiver_info = &drupal_static(__FUNCTION__, array());
+
+  if (empty($archiver_info)) {
+    $cache = cache_get('archiver_info');
+    if ($cache === FALSE) {
+      // Rebuild the cache and save it.
+      $archiver_info = module_invoke_all('archiver_info');
+      drupal_alter('archiver_info', $archiver_info);
+      uasort($archiver_info, 'drupal_sort_weight');
+      cache_set('archiver_info', $archiver_info);
+    }
+    else {
+      $archiver_info = $cache->data;
+    }
+  }
+
+  return $archiver_info;
+}
+
+/**
+ * Create the appropriate archiver for the specified file.
+ *
+ * @param $file
+ *   The full path of the archive file.  Note that stream wrapper
+ *   paths are supported.
+ * @return
+ *   A newly created instance of the archiver class appropriate
+ *   for the specified file, already bound to that file.
+ */
+function archiver_get_archiver($file) {
+  $archiver_info = archiver_get_info();
+
+  foreach ($archiver_info as $implementation) {
+   foreach ($implementation['extensions'] as $extension) {
+     // Because extensions may be multi-part, such as .tar.gz,
+     // we cannot use simpler approaches like substr() or pathinfo().
+     // This method isn't quite as clean but gets the job done.
+     // Also note that the file may not yet exist, so we cannot rely
+     // on fileinfo() or other disk-level utilities.
+     if (strrpos($file, $extension) === strlen($file) + strlen($ext)) {
+       return new $implementation['class']($file);
+     }
+   }
+  }
+}
+

=== modified file 'modules/system/system.api.php'
--- modules/system/system.api.php	2009-10-14 10:56:35 +0000
+++ modules/system/system.api.php	2009-10-15 07:01:28 +0000
@@ -2288,7 +2288,7 @@ function hook_drupal_goto_alter(array $a
  *   the Drupal installation process that occurs after the installation profile
  *   is selected.
  * @param $install_state
- *   An array of information about the current installation state. 
+ *   An array of information about the current installation state.
  */
 function hook_install_tasks_alter(&$tasks, $install_state) {
   // Replace the "Choose language" installation task provided by Drupal core
@@ -2403,6 +2403,34 @@ function hook_action_info_alter(&$action
 }
 
 /**
+ * Declare archivers to the system.
+ *
+ * An archiver is a class that is able to package and unpackage one or more files
+ * into a single possibly compressed file.  Common examples of such files are
+ * zip files and tar.gz files.  All archiver classes must implement
+ * ArchiverInterface.
+ *
+ * When mapping a
+ *
+ * Each entry should be keyed on a unique value, and specify three
+ * additional keys:
+ *   - class: The name of the PHP class for this archiver.
+ *   - extensions: An array of file extensions that this archiver supports.
+ *   - weight: This optional key specifies the weight of this archiver.
+ *     When mapping file extensions to archivers, the first archiver by
+ *     weight found that supports the requested extension will be used.
+ */
+function system_archiver_info() {
+  return array(
+    'tar' => array(
+      'class' => 'ArchiverTar',
+      'extensions' => array('tar', 'tar.gz', 'tar.bz2'),
+    ),
+  );
+}
+
+
+/**
  * Defines additional date types.
  *
  * Next to the 'long', 'medium' and 'short' date types defined in core, any
@@ -2435,7 +2463,7 @@ function hook_date_format_types() {
  * module can define additional types that can be used when displaying dates. A
  * date type is a key which can be passed to format_date() to return a date in
  * the configured displayed format. A date format is a string defining the date
- * and time elements to use. For example, a date type could be 
+ * and time elements to use. For example, a date type could be
  * 'mymodule_extra_long', while a date format is like 'Y-m-d'.
  *
  * New date types must first be declared using hook_date_format_types(). It is
@@ -2463,7 +2491,7 @@ function hook_date_format_types() {
  *     'short', 'mymodule_extra_long'. It must first be declared in
  *     hook_date_format_types() unless extending a type provided by another
  *     module.
- *   - 'format': a string defining the date and time elements to use. It 
+ *   - 'format': a string defining the date and time elements to use. It
  *     can contain any of the formatting options described at
  *     http://php.net/manual/en/function.date.php
  *   - 'locales': (optional) an array of 2 and 5 character language codes, for

=== added file 'modules/system/system.archiver.inc'
--- modules/system/system.archiver.inc	1970-01-01 00:00:00 +0000
+++ modules/system/system.archiver.inc	2009-10-15 07:14:59 +0000
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Archiver implementations provided by the system module.
+ */
+
+/**
+ * Archiver for .tar files.
+ */
+class ArchiverTar implements ArchiverInterface {
+
+  /**
+   * The underlying Archive_Tar instance that does the heavy lifting.
+   *
+   * @var Archive_Tar
+   */
+  protected $tar;
+
+  public function __construct($file_path) {
+    $this->tar = new Archive_Tar($file_path);
+  }
+
+  public function add($file_path) {
+    $this->tar->add($file_path);
+
+    return $this;
+  }
+
+  public function remove($path) {
+    // @todo Archive_Tar doesn't have a remove operation
+    // so we'll have to simulate it somehow, probably by
+    // creating a new archive with everything but the removed
+    // file.
+
+    return $this;
+  }
+
+  public function extract($path, Array $files = array()) {
+    if ($files) {
+      $this->tar->extractList($files, $path);
+    }
+    else {
+      $this->tar->extract($path);
+    }
+
+    return $this;
+  }
+
+  public function listContents() {
+    return $this->tar->listContent();
+  }
+
+  /**
+   * Retrieve the tar engine itself.
+   *
+   * In some cases it may be necessary to directly access the underlying
+   * Archive_Tar object for implementation-specific logic. This is for advanced
+   * use only as it is not shared by other implementations of ArchiveInterface.
+   *
+   * @return
+   *   The Archive_Tar object used by this object.
+   */
+  public function getArchive() {
+    return $this->tar;
+  }
+}

=== modified file 'modules/system/system.info'
--- modules/system/system.info	2009-08-31 18:30:26 +0000
+++ modules/system/system.info	2009-10-15 03:42:15 +0000
@@ -6,6 +6,7 @@ version = VERSION
 core = 7.x
 files[] = system.module
 files[] = system.admin.inc
+files[] = system.archiver.inc
 files[] = system.queue.inc
 files[] = image.gd.inc
 files[] = system.install

=== modified file 'modules/system/system.module'
--- modules/system/system.module	2009-10-14 20:42:47 +0000
+++ modules/system/system.module	2009-10-15 07:01:01 +0000
@@ -1746,7 +1746,7 @@ function system_admin_menu_block($item) 
   $has_subitems = FALSE;
   $result = db_query("
     SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, m.description, m.path, m.weight as router_weight, ml.*
-    FROM {menu_router} m 
+    FROM {menu_router} m
     LEFT JOIN {menu_links} ml ON m.path = ml.router_path
     WHERE (ml.plid = :plid AND ml.menu_name = :name AND hidden = 0) OR (m.tab_parent = :path AND m.type IN (:local_task, :default_task))", array(':plid' => $item['mlid'], ':name' => $item['menu_name'], ':path' => $item['path'], ':local_task' => MENU_LOCAL_TASK, ':default_task' => MENU_DEFAULT_LOCAL_TASK), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $link) {
@@ -1780,7 +1780,7 @@ function system_admin_menu_block($item) 
     }
   }
   if ($has_subitems) {
-    // If we've had at least one non-tab subitem, remove the link for the 
+    // If we've had at least one non-tab subitem, remove the link for the
     // default task, since that is already broken down to subitems.
     unset($content[$default_task]);
   }
@@ -3364,3 +3364,14 @@ function system_date_format_delete($dfid
     ->execute();
 }
 
+/**
+ * Implement hook_archiver_info().
+ */
+function system_archiver_info() {
+  return array(
+    'tar' => array(
+      'class' => 'ArchiverTar',
+      'extensions' => array('tar', 'tar.gz', 'tar.bz2'),
+    ),
+  );
+}

