فهرست منبع

ENH: Merge from the cpack branch

Andy Cedilnik 20 سال پیش
والد
کامیت
d0329919da

+ 32 - 0
Source/CPack/cmCPackConfigure.h.in

@@ -0,0 +1,32 @@
+#cmakedefine HAVE_WCHAR_H @HAVE_WCHAR_H@
+#cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@
+#cmakedefine HAVE_IO_H @HAVE_IO_H@
+#cmakedefine HAVE_SIZEOF_SSIZE_T @HAVE_SIZEOF_SSIZE_T@
+#if !defined(HAVE_SIZEOF_SSIZE_T)
+#  define ssize_t @ssize_t@
+#endif
+#cmakedefine HAVE_SIZEOF_INT64_T @HAVE_SIZEOF_INT64_T@
+#if !defined(HAVE_SIZEOF_INT64_T)
+#  define int64_t @int64_t@
+#  define uint64_t unsigned @int64_t@
+#endif
+#cmakedefine HAVE_SIZEOF_UID_T @HAVE_SIZEOF_UID_T@
+#if !defined(HAVE_SIZEOF_UID_T)
+#  define uid_t @uid_t@
+#endif
+#cmakedefine HAVE_SIZEOF_GID_T @HAVE_SIZEOF_GID_T@
+#if !defined(HAVE_SIZEOF_GID_T)
+#  define gid_t @gid_t@
+#endif
+#cmakedefine HAVE_SIZEOF_INTMAX_T @HAVE_SIZEOF_INTMAX_T@
+#if !defined(HAVE_SIZEOF_INTMAX_T)
+#  define intmax_t @intmax_t@
+#endif
+#cmakedefine HAVE_SIZEOF_UINTMAX_T @HAVE_SIZEOF_UINTMAX_T@
+#if !defined(HAVE_SIZEOF_UINTMAX_T)
+#  define uintmax_t @uintmax_t@
+#endif
+#cmakedefine HAVE_GETEUID @HAVE_GETEUID@
+
+#cmakedefine HAVE_INTTYPES_H @HAVE_INTTYPES_H@
+

+ 106 - 0
Source/CPack/cmCPackGenerators.cxx

@@ -0,0 +1,106 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCPackGenerators.h"
+
+#include "cmCPackGenericGenerator.h"
+#include "cmCPackTGZGenerator.h"
+#include "cmCPackSTGZGenerator.h"
+#include "cmCPackNSISGenerator.h"
+#include "cmCPackPackageMakerGenerator.h"
+
+//----------------------------------------------------------------------
+cmCPackGenerators::cmCPackGenerators()
+{
+  this->RegisterGenerator("TGZ", cmCPackTGZGenerator::CreateGenerator);
+  this->RegisterGenerator("STGZ", cmCPackSTGZGenerator::CreateGenerator);
+  this->RegisterGenerator("NSIS", cmCPackNSISGenerator::CreateGenerator);
+  this->RegisterGenerator("PackageMaker", cmCPackPackageMakerGenerator::CreateGenerator);
+}
+
+//----------------------------------------------------------------------
+cmCPackGenerators::~cmCPackGenerators()
+{
+  std::vector<cmCPackGenericGenerator*>::iterator it;
+  for ( it = m_Generators.begin(); it != m_Generators.end(); ++ it )
+    {
+    delete *it;
+    }
+}
+
+//----------------------------------------------------------------------
+cmCPackGenericGenerator* cmCPackGenerators::NewGenerator(const char* name)
+{
+  cmCPackGenericGenerator* gen = this->NewGeneratorInternal(name);
+  if ( !gen )
+    {
+    return 0;
+    }
+  if ( !gen->Initialize(name) )
+    {
+    delete gen;
+    return 0;
+    }
+  m_Generators.push_back(gen);
+  return gen;
+}
+
+//----------------------------------------------------------------------
+cmCPackGenericGenerator* cmCPackGenerators::NewGeneratorInternal(const char* name)
+{
+  if ( !name )
+    {
+    return 0;
+    }
+  cmCPackGenerators::t_GeneratorCreatorsMap::iterator it = m_GeneratorCreators.find(name);
+  if ( it == m_GeneratorCreators.end() )
+    {
+    return 0;
+    }
+  return (it->second)();
+  /*
+  std::string sname = name;
+  if ( sname == "STGZ" )
+    {
+    return new cmCPackSTGZGenerator;
+    }
+  if ( sname == "TGZ" )
+    {
+    return new cmCPackTGZGenerator;
+    }
+  if ( sname == "NSIS" )
+    {
+    return new cmCPackNSISGenerator;
+    }
+  if ( sname == "PackageMaker" )
+    {
+    return new cmCPackPackageMakerGenerator;
+    }
+  return new cmCPackGenericGenerator;
+  */
+}
+
+//----------------------------------------------------------------------
+void cmCPackGenerators::RegisterGenerator(const char* name, CreateGeneratorCall* createGenerator)
+{
+  if ( !name || !createGenerator )
+    {
+    std::cerr << "Cannot register generator" << std::endl;
+    return;
+    }
+  m_GeneratorCreators[name] = createGenerator;
+}

+ 56 - 0
Source/CPack/cmCPackGenerators.h

@@ -0,0 +1,56 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackGenerators_h
+#define cmCPackGenerators_h
+
+#include "cmObject.h"
+
+class cmCPackGenericGenerator;
+
+/** \class cmCPackGenerators
+ * \brief A container for CPack generators
+ *
+ */
+class cmCPackGenerators : public cmObject
+{
+public:
+  cmTypeMacro(cmCPackGenerators, cmObject);
+
+  cmCPackGenerators();
+  ~cmCPackGenerators();
+
+  //! Get the generator
+  cmCPackGenericGenerator* NewGenerator(const char* name);
+  void DeleteGenerator(cmCPackGenericGenerator* gen);
+
+  typedef cmCPackGenericGenerator* CreateGeneratorCall();
+
+  void RegisterGenerator(const char* name, CreateGeneratorCall* createGenerator);
+
+private:
+  cmCPackGenericGenerator* NewGeneratorInternal(const char* name);
+  std::vector<cmCPackGenericGenerator*> m_Generators;
+
+  typedef std::map<cmStdString, CreateGeneratorCall*> t_GeneratorCreatorsMap;
+  t_GeneratorCreatorsMap m_GeneratorCreators;
+};
+
+#endif
+
+
+

+ 475 - 0
Source/CPack/cmCPackGenericGenerator.cxx

@@ -0,0 +1,475 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCPackGenericGenerator.h"
+
+#include "cmake.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+
+#include <cmsys/SystemTools.hxx>
+#include <cmsys/Glob.hxx>
+#include <memory> // auto_ptr
+
+//----------------------------------------------------------------------
+cmCPackGenericGenerator::cmCPackGenericGenerator()
+{
+  m_GeneratorVerbose = false;
+  m_GlobalGenerator = 0;
+  m_LocalGenerator = 0;
+  m_MakefileMap = 0;
+  m_CMakeInstance = 0;
+}
+
+//----------------------------------------------------------------------
+cmCPackGenericGenerator::~cmCPackGenericGenerator()
+{
+  if ( m_GlobalGenerator )
+    {
+    delete m_GlobalGenerator;
+    m_GlobalGenerator = 0;
+    }
+  if ( m_LocalGenerator )
+    {
+    delete m_LocalGenerator;
+    m_LocalGenerator = 0;
+    }
+  if ( m_CMakeInstance )
+    {
+    delete m_CMakeInstance;
+    m_CMakeInstance = 0;
+    }
+  m_MakefileMap = 0;
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::PrepareNames()
+{
+  std::string tempDirectory = this->GetOption("CPACK_PROJECT_DIRECTORY");
+  tempDirectory += "/_CPack_Packages/";
+  tempDirectory += this->GetOption("CPACK_GENERATOR");
+  std::string topDirectory = tempDirectory;
+
+  std::string outName = this->GetOption("CPACK_PROJECT_NAME");
+  outName += "-";
+  outName += this->GetOption("CPACK_PROJECT_VERSION");
+  const char* patch = this->GetOption("CPACK_PROJECT_VERSION_PATCH");
+  if ( patch && *patch )
+    {
+    outName += "-";
+    outName += patch;
+    }
+  const char* postfix = this->GetOutputPostfix();
+  if ( postfix && *postfix )
+    {
+    outName += "-";
+    outName += postfix;
+    }
+  tempDirectory += "/" + outName;
+
+  outName += ".";
+  outName += this->GetOutputExtension();
+
+
+  std::string installFile = this->GetOption("CPACK_PROJECT_DIRECTORY");
+  installFile += "/cmake_install.cmake";
+
+  std::string destFile = this->GetOption("CPACK_PROJECT_DIRECTORY");
+  destFile += "/" + outName;
+
+  std::string outFile = topDirectory + "/" + outName;
+  std::string installPrefix = tempDirectory + this->GetInstallPrefix();
+
+  this->SetOption("CPACK_TOPLEVEL_DIRECTORY", topDirectory.c_str());
+  this->SetOption("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str());
+  this->SetOption("CPACK_INSTALL_FILE_NAME", installFile.c_str());
+  this->SetOption("CPACK_OUTPUT_FILE_NAME", outName.c_str());
+  this->SetOption("CPACK_PACKAGE_FILE_NAME", destFile.c_str());
+  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", outFile.c_str());
+  this->SetOption("CPACK_INSTALL_DIRECTORY", this->GetInstallPath());
+  this->SetOption("CPACK_NATIVE_INSTALL_DIRECTORY",
+    cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
+  this->SetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY", installPrefix.c_str());
+
+  std::cout << "Look for: CPACK_PROJECT_DESCRIPTION_FILE_NAME" << std::endl;
+  const char* descFileName = this->GetOption("CPACK_PROJECT_DESCRIPTION_FILE_NAME");
+  std::cout << "Look for: " << descFileName << std::endl;
+  if ( descFileName )
+    {
+    if ( !cmSystemTools::FileExists(descFileName) )
+      {
+      std::cout << "Cannot find description file name: " << descFileName << std::endl;
+      return 0;
+      }
+    std::ifstream ifs(descFileName);
+    if ( !ifs )
+      {
+      std::cout << "Cannot open description file name: " << descFileName << std::endl;
+      return 0;
+      }
+    cmOStringStream ostr;
+    std::string line;
+    while ( ifs && cmSystemTools::GetLineFromStream(ifs, line) )
+      {
+      ostr << cmSystemTools::MakeXMLSafe(line.c_str()) << std::endl;
+      }
+    this->SetOption("CPACK_PROJECT_DESCRIPTION", ostr.str().c_str());
+    }
+  if ( !this->GetOption("CPACK_PROJECT_DESCRIPTION") )
+    {
+    std::cout << "Project description not specified. Please specify CPACK_PROJECT_DESCRIPTION or CPACK_PROJECT_DESCRIPTION_FILE_NAME." << std::endl;
+    return 0;
+    }
+
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::InstallProject()
+{
+  std::cout << "Install project" << std::endl;
+  const char* tempInstallDirectory = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
+  const char* installFile = this->GetOption("CPACK_INSTALL_FILE_NAME");
+  if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory))
+    {
+    std::cerr << "Problem creating temporary directory: " << tempInstallDirectory << std::endl;
+    return 0;
+    }
+  cmake cm;
+  cmGlobalGenerator gg;
+  gg.SetCMakeInstance(&cm);
+  std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
+  lg->SetGlobalGenerator(&gg);
+  cmMakefile *mf = lg->GetMakefile();
+  bool movable = true;
+  if ( movable )
+    {
+    mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
+    }
+  const char* buildConfig = this->GetOption("CPACK_BUILD_CONFIG");
+  if ( buildConfig && *buildConfig )
+    {
+    mf->AddDefinition("BUILD_TYPE", buildConfig);
+    }
+
+  if ( movable )
+    {
+    // Make sure there is no destdir
+    cmSystemTools::PutEnv("DESTDIR=");
+    }
+  else
+    {
+    std::string destDir = "DESTDIR=";
+    destDir += tempInstallDirectory;
+    cmSystemTools::PutEnv(destDir.c_str());
+    }
+  int res = mf->ReadListFile(0, installFile);
+  if ( !movable )
+    {
+    cmSystemTools::PutEnv("DESTDIR=");
+    }
+  return res;
+}
+
+//----------------------------------------------------------------------
+void cmCPackGenericGenerator::SetOption(const char* op, const char* value)
+{
+  if ( !op )
+    {
+    return;
+    }
+  if ( !value )
+    {
+    m_MakefileMap->RemoveDefinition(op);
+    return;
+    }
+  std::cout << this->GetNameOfClass() << "::SetOption(" << op << ", " << value << ")" << std::endl;
+  m_MakefileMap->AddDefinition(op, value);
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::ProcessGenerator()
+{
+  if ( !this->PrepareNames() )
+    {
+    return 0;
+    }
+  if ( !this->InstallProject() )
+    {
+    return 0;
+    }
+
+  const char* tempPackageFileName = this->GetOption(
+    "CPACK_TEMPORARY_PACKAGE_FILE_NAME");
+  const char* packageFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
+  const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+
+
+  std::cout << "Find files" << std::endl;
+  cmsys::Glob gl;
+  std::string findExpr = tempDirectory;
+  findExpr += "/*";
+  gl.RecurseOn();
+  if ( !gl.FindFiles(findExpr) )
+    {
+    std::cerr << "CPack error: cannot find any files in the packaging tree" << std::endl;
+    return 0;
+    }
+
+  std::cout << "Compress files to: " << tempPackageFileName << std::endl;
+  if ( !this->CompressFiles(tempPackageFileName,
+      tempDirectory, gl.GetFiles()) )
+    {
+    std::cerr << "CPack error: problem compressing the directory" << std::endl;
+    return 0;
+    }
+
+  std::cout << "Finalize package" << std::endl;
+  std::cout << "Copy final package: " << tempPackageFileName << " to " << packageFileName << std::endl;
+  if ( !cmSystemTools::CopyFileIfDifferent(tempPackageFileName, packageFileName) )
+    {
+    std::cerr << "CPack error: problem copying the package: " << tempPackageFileName << " to " << packageFileName << std::endl;
+    return 0;
+    }
+
+  std::cout << "All done" << std::endl;
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::Initialize(const char* name)
+{
+  m_CMakeInstance = new cmake;
+  m_CMakeInstance->AddCMakePaths(m_CMakeRoot.c_str());
+  m_GlobalGenerator = new cmGlobalGenerator;
+  m_GlobalGenerator->SetCMakeInstance(m_CMakeInstance);
+  m_LocalGenerator = m_GlobalGenerator->CreateLocalGenerator();
+  m_MakefileMap = m_LocalGenerator->GetMakefile();
+  m_Name = name;
+  this->SetOption("CPACK_GENERATOR", name);
+  return 1;
+}
+
+//----------------------------------------------------------------------
+const char* cmCPackGenericGenerator::GetOption(const char* op)
+{
+  return m_MakefileMap->GetDefinition(op);
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::GenerateHeader(std::ostream* os)
+{
+  (void)os;
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::FindRunningCMake(const char* arg0)
+{
+  int found = 0;
+  // Find our own executable.
+  std::vector<cmStdString> failures;
+  m_CPackSelf = arg0;
+  cmSystemTools::ConvertToUnixSlashes(m_CPackSelf);
+  failures.push_back(m_CPackSelf);
+  m_CPackSelf = cmSystemTools::FindProgram(m_CPackSelf.c_str());
+  if(!cmSystemTools::FileExists(m_CPackSelf.c_str()))
+    {
+    failures.push_back(m_CPackSelf);
+    m_CPackSelf =  "/usr/local/bin/ctest";
+    }
+  if(!cmSystemTools::FileExists(m_CPackSelf.c_str()))
+    {
+    failures.push_back(m_CPackSelf);
+    cmOStringStream msg;
+    msg << "CTEST can not find the command line program ctest.\n";
+    msg << "  argv[0] = \"" << arg0 << "\"\n";
+    msg << "  Attempted paths:\n";
+    std::vector<cmStdString>::iterator i;
+    for(i=failures.begin(); i != failures.end(); ++i)
+      {
+      msg << "    \"" << i->c_str() << "\"\n";
+      }
+    cmSystemTools::Error(msg.str().c_str());
+    }
+  std::string dir;
+  std::string file;
+  if(cmSystemTools::SplitProgramPath(m_CPackSelf.c_str(),
+                                     dir, file, true))
+    {
+    m_CMakeSelf = dir += "/cmake";
+    m_CMakeSelf += cmSystemTools::GetExecutableExtension();
+    if(cmSystemTools::FileExists(m_CMakeSelf.c_str()))
+      {
+      found = 1;
+      }
+    }
+  if ( !found )
+    {
+    failures.push_back(m_CMakeSelf);
+#ifdef CMAKE_BUILD_DIR
+    std::string intdir = ".";
+#ifdef  CMAKE_INTDIR
+    intdir = CMAKE_INTDIR;
+#endif
+    m_CMakeSelf = CMAKE_BUILD_DIR;
+    m_CMakeSelf += "/bin/";
+    m_CMakeSelf += intdir;
+    m_CMakeSelf += "/cmake";
+    m_CMakeSelf += cmSystemTools::GetExecutableExtension();
+#endif
+    if(!cmSystemTools::FileExists(m_CMakeSelf.c_str()))
+      {
+      failures.push_back(m_CMakeSelf);
+      cmOStringStream msg;
+      msg << "CTEST can not find the command line program cmake.\n";
+      msg << "  argv[0] = \"" << arg0 << "\"\n";
+      msg << "  Attempted paths:\n";
+      std::vector<cmStdString>::iterator i;
+      for(i=failures.begin(); i != failures.end(); ++i)
+        {
+        msg << "    \"" << i->c_str() << "\"\n";
+        }
+      cmSystemTools::Error(msg.str().c_str());
+      }
+    }
+  // do CMAKE_ROOT, look for the environment variable first
+  std::string cMakeRoot;
+  std::string modules;
+  if (getenv("CMAKE_ROOT"))
+    {
+    cMakeRoot = getenv("CMAKE_ROOT");
+    modules = cMakeRoot + "/Modules/CMake.cmake";
+    }
+  if(!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // next try exe/..
+    cMakeRoot  = cmSystemTools::GetProgramPath(m_CMakeSelf.c_str());
+    std::string::size_type slashPos = cMakeRoot.rfind("/");
+    if(slashPos != std::string::npos)      
+      {
+      cMakeRoot = cMakeRoot.substr(0, slashPos);
+      }
+    // is there no Modules direcory there?
+    modules = cMakeRoot + "/Modules/CMake.cmake"; 
+    }
+  
+  if (!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // try exe/../share/cmake
+    cMakeRoot += CMAKE_DATA_DIR;
+    modules = cMakeRoot + "/Modules/CMake.cmake";
+    }
+#ifdef CMAKE_ROOT_DIR
+  if (!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // try compiled in root directory
+    cMakeRoot = CMAKE_ROOT_DIR;
+    modules = cMakeRoot + "/Modules/CMake.cmake";
+    }
+#endif
+#ifdef CMAKE_PREFIX
+  if (!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // try compiled in install prefix
+    cMakeRoot = CMAKE_PREFIX CMAKE_DATA_DIR;
+    modules = cMakeRoot + "/Modules/CMake.cmake";
+    }
+#endif
+  if (!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // try 
+    cMakeRoot  = cmSystemTools::GetProgramPath(m_CMakeSelf.c_str());
+    cMakeRoot += CMAKE_DATA_DIR;
+    modules = cMakeRoot +  "/Modules/CMake.cmake";
+    }
+  if(!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // next try exe
+    cMakeRoot  = cmSystemTools::GetProgramPath(m_CMakeSelf.c_str());
+    // is there no Modules direcory there?
+    modules = cMakeRoot + "/Modules/CMake.cmake"; 
+    }
+  if (!cmSystemTools::FileExists(modules.c_str()))
+    {
+    // couldn't find modules
+    cmSystemTools::Error("Could not find CMAKE_ROOT !!!\n"
+                         "CMake has most likely not been installed correctly.\n"
+                         "Modules directory not found in\n",
+                         cMakeRoot.c_str());
+    return 0;
+    }
+  m_CMakeRoot = cMakeRoot;
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackGenericGenerator::CompressFiles(const char* outFileName, const char* toplevel,
+    const std::vector<std::string>& files)
+{
+  (void)outFileName;
+  (void)toplevel;
+  (void)files;
+  return 0;
+}
+
+//----------------------------------------------------------------------
+const char* cmCPackGenericGenerator::GetInstallPath()
+{
+  if ( !m_InstallPath.empty() )
+    {
+    return m_InstallPath.c_str();
+    }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+  const char* prgfiles = cmsys::SystemTools::GetEnv("ProgramFiles");
+  const char* sysDrive = cmsys::SystemTools::GetEnv("SystemDrive");
+  if ( prgfiles )
+    {
+    m_InstallPath = prgfiles;
+    }
+  else if ( sysDrive )
+    {
+    m_InstallPath = sysDrive;
+    m_InstallPath += "/Program Files";
+    }
+  else 
+    {
+    m_InstallPath = "c:/Program Files";
+    }
+  m_InstallPath += "/";
+  m_InstallPath += this->GetOption("CPACK_PROJECT_NAME");
+  m_InstallPath += "-";
+  m_InstallPath += this->GetOption("CPACK_PROJECT_VERSION");
+#else
+  m_InstallPath = "/usr/local/";
+#endif
+  return m_InstallPath.c_str();
+}
+
+//----------------------------------------------------------------------
+std::string cmCPackGenericGenerator::FindTemplate(const char* name)
+{
+  return m_MakefileMap->GetModulesFile(name);
+}
+
+//----------------------------------------------------------------------
+bool cmCPackGenericGenerator::ConfigureFile(const char* inName, const char* outName)
+{
+  return m_MakefileMap->ConfigureFile(inName, outName, false, true, false);
+}
+

+ 103 - 0
Source/CPack/cmCPackGenericGenerator.h

@@ -0,0 +1,103 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackGenericGenerator_h
+#define cmCPackGenericGenerator_h
+
+
+#include "cmObject.h"
+
+#define cmCPackTypeMacro(class, superclass) \
+  cmTypeMacro(class, superclass); \
+  static cmCPackGenericGenerator* CreateGenerator() { return new class; }
+
+class cmMakefile;
+class cmLocalGenerator;
+class cmGlobalGenerator;
+class cmake;
+
+/** \class cmCPackGenericGenerator
+ * \brief A superclass of all CPack Generators
+ *
+ */
+class cmCPackGenericGenerator : public cmObject
+{
+public:
+  cmTypeMacro(cmCPackGenericGenerator, cmObject);
+  /**
+   * If verbose then more informaiton is printed out
+   */
+  void SetVerbose(bool val) { m_GeneratorVerbose = val; }
+
+  /**
+   * Do the actual processing. Subclass has to override it.
+   * Return 0 if error.
+   */
+  virtual int ProcessGenerator();
+
+  /**
+   * Initialize generator
+   */
+  virtual int Initialize(const char* name);
+
+  /**
+   * Construct generator
+   */
+  cmCPackGenericGenerator();
+  virtual ~cmCPackGenericGenerator();
+
+  //! Set and get the options
+  void SetOption(const char* op, const char* value);
+  const char* GetOption(const char* op);
+
+  //! Set all the variables
+  int FindRunningCMake(const char* arg0);
+
+protected:
+  int PrepareNames();
+  int InstallProject();
+  virtual int GenerateHeader(std::ostream* os);
+
+  virtual const char* GetOutputExtension() { return "cpack"; }
+  virtual const char* GetOutputPostfix() { return 0; }
+  virtual int CompressFiles(const char* outFileName, const char* toplevel,
+    const std::vector<std::string>& files);
+  virtual const char* GetInstallPath();
+  virtual const char* GetInstallPrefix() { return "/"; }
+
+  virtual std::string FindTemplate(const char* name);
+  virtual bool ConfigureFile(const char* inName, const char* outName);
+
+  bool m_GeneratorVerbose;
+  std::string m_Name;
+
+  std::string m_InstallPath;
+
+  std::string m_CPackSelf;
+  std::string m_CMakeSelf;
+  std::string m_CMakeRoot;
+
+private:
+  cmGlobalGenerator* m_GlobalGenerator;
+  cmLocalGenerator* m_LocalGenerator;
+  cmMakefile* m_MakefileMap;
+  cmake* m_CMakeInstance;
+};
+
+#endif
+
+

+ 108 - 0
Source/CPack/cmCPackNSISGenerator.cxx

@@ -0,0 +1,108 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCPackNSISGenerator.h"
+
+#include "cmake.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmSystemTools.h"
+#include "cmMakefile.h"
+#include "cmGeneratedFileStream.h"
+
+#include <cmsys/SystemTools.hxx>
+#include <cmsys/Glob.hxx>
+
+//----------------------------------------------------------------------
+cmCPackNSISGenerator::cmCPackNSISGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+cmCPackNSISGenerator::~cmCPackNSISGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+int cmCPackNSISGenerator::ProcessGenerator()
+{
+  return this->Superclass::ProcessGenerator();
+}
+
+//----------------------------------------------------------------------
+int cmCPackNSISGenerator::CompressFiles(const char* outFileName, const char* toplevel,
+  const std::vector<std::string>& files)
+{
+  (void)outFileName; // TODO: Fix nsis to force out file name
+  (void)toplevel;
+  (void)files;
+  std::string nsisInFileName = this->FindTemplate("NSIS.template.in");
+  if ( nsisInFileName.size() == 0 )
+    {
+    std::cerr << "CPack error: Could not find NSIS installer template file." << std::endl;
+    return false;
+    }
+  std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  std::string tmpFile = nsisFileName;
+  tmpFile += "/NSISOutput.log";
+  nsisFileName += "/project.nsi";
+  std::cout << "Configure file: " << nsisInFileName << " to " << nsisFileName << std::endl;
+  this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
+  std::string nsisCmd = "\"";
+  nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM");
+  nsisCmd += "\" \"" + nsisFileName + "\"";
+  std::cout << "Execute: " << nsisCmd.c_str() << std::endl;
+  std::string output;
+  int retVal = 1;
+  bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output, &retVal, 0, m_GeneratorVerbose, 0);
+  if ( !res || retVal )
+    {
+    cmGeneratedFileStream ofs(tmpFile.c_str());
+    ofs << "# Run command: " << nsisCmd.c_str() << std::endl
+      << "# Output:" << std::endl
+      << output.c_str() << std::endl;
+    std::cerr << "Problem running NSIS command: " << nsisCmd.c_str() << std::endl;
+    std::cerr << "Please check " << tmpFile.c_str() << " for errors" << std::endl;
+    return 0;
+    }
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackNSISGenerator::Initialize(const char* name)
+{
+  std::cout << "cmCPackNSISGenerator::Initialize()" << std::endl;
+  int res = this->Superclass::Initialize(name);
+  std::vector<std::string> path;
+  std::string nsisPath;
+  if ( !cmsys::SystemTools::ReadRegistryValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS",
+      nsisPath) )
+    {
+    std::cerr << "Cannot find NSIS registry value" << std::endl;
+    return 0;
+    }
+  path.push_back(nsisPath);
+  nsisPath = cmSystemTools::FindProgram("makensis", path, false);
+  if ( nsisPath.empty() )
+    {
+    std::cerr << "Cannot find NSIS compiler" << std::endl;
+    return 0;
+    }
+  this->SetOption("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
+  return res;
+}
+

+ 60 - 0
Source/CPack/cmCPackNSISGenerator.h

@@ -0,0 +1,60 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackNSISGenerator_h
+#define cmCPackNSISGenerator_h
+
+
+#include "cmCPackGenericGenerator.h"
+#include "CPack/cmCPackConfigure.h" // for ssize_t
+
+/** \class cmCPackNSISGenerator
+ * \brief A generator for NSIS files
+ *
+ * http://people.freebsd.org/~kientzle/libarchive/
+ */
+class cmCPackNSISGenerator : public cmCPackGenericGenerator
+{
+public:
+  cmCPackTypeMacro(cmCPackNSISGenerator, cmCPackGenericGenerator);
+  /**
+   * Do the actual processing. Subclass has to override it.
+   * Return < 0 if error.
+   */
+  virtual int ProcessGenerator();
+
+  /**
+   * Initialize generator
+   */
+  virtual int Initialize(const char* name);
+
+  /**
+   * Construct generator
+   */
+  cmCPackNSISGenerator();
+  virtual ~cmCPackNSISGenerator();
+
+protected:
+  int CompressFiles(const char* outFileName, const char* toplevel,
+    const std::vector<std::string>& files);
+  virtual const char* GetOutputExtension() { return "exe"; }
+  virtual const char* GetOutputPostfix() { return "win32"; }
+};
+
+#endif
+
+

+ 203 - 0
Source/CPack/cmCPackPackageMakerGenerator.cxx

@@ -0,0 +1,203 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmCPackPackageMakerGenerator.h"
+
+#include "cmake.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmSystemTools.h"
+#include "cmMakefile.h"
+#include "cmGeneratedFileStream.h"
+
+#include <cmsys/SystemTools.hxx>
+#include <cmsys/Glob.hxx>
+
+//----------------------------------------------------------------------
+cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+int cmCPackPackageMakerGenerator::ProcessGenerator()
+{
+  return this->Superclass::ProcessGenerator();
+}
+
+//----------------------------------------------------------------------
+int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName, const char* toplevel,
+  const std::vector<std::string>& files)
+{
+  // Create directory structure
+  std::string resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  resDir += "/Resources";
+  std::string preflightDirName = resDir + "/PreFlight";
+  std::string postflightDirName = resDir + "/PostFlight";
+  
+  if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str())
+    || !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()) )
+    {
+    std::cerr << "Problem creating installer directories: " << preflightDirName.c_str() << " and " << postflightDirName.c_str() << std::endl;
+    return 0;
+    }
+
+  if ( !this->CopyCreateResourceFile("License")
+    || !this->CopyCreateResourceFile("ReadMe")
+    || !this->CopyCreateResourceFile("Welcome") 
+    || !this->CopyResourcePlistFile("Info.plist")
+    || !this->CopyResourcePlistFile("Description.plist") )
+    {
+    std::cerr << "Problem copying the resource files" << std::endl;
+    return 0;
+    }
+
+  std::string packageDirFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+  packageDirFileName += ".pkg";
+
+  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  tmpFile += "/PackageMakerOutput.log";
+  cmOStringStream pkgCmd;
+  /*
+  pkgCmd << "sh -c '\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+  << "\" -build -p \"" << packageDirFileName << "\" -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
+  << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Resources\" -i \""
+  << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
+  << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"'";
+  */
+  pkgCmd << "/Users/kitware/Andy/CMake-CPack/foo.sh";
+  std::cout << "Execute: " << pkgCmd.str().c_str() << std::endl;
+  std::string output;
+  int retVal = 1;
+  //bool res = cmSystemTools::RunSingleCommand(pkgCmd.str().c_str(), &output, &retVal, 0, m_GeneratorVerbose, 0);
+  bool res = true;
+  retVal = system(pkgCmd.str().c_str());
+  std::cout << "Done running package maker" << std::endl;
+  if ( !res || retVal )
+    {
+    cmGeneratedFileStream ofs(tmpFile.c_str());
+    ofs << "# Run command: " << pkgCmd.str().c_str() << std::endl
+      << "# Output:" << std::endl
+      << output.c_str() << std::endl;
+    std::cerr << "Problem running PackageMaker command: " << pkgCmd.str().c_str() << std::endl;
+    std::cerr << "Please check " << tmpFile.c_str() << " for errors" << std::endl;
+    return 0;
+    }
+
+  tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  tmpFile += "/hdiutilOutput.log";
+  cmOStringStream dmgCmd;
+  dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
+    << "\" \"" << outFileName << "\"";
+  res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, &retVal, 0, m_GeneratorVerbose, 0);
+  if ( !res || retVal )
+    {
+    cmGeneratedFileStream ofs(tmpFile.c_str());
+    ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
+      << "# Output:" << std::endl
+      << output.c_str() << std::endl;
+    std::cerr << "Problem running hdiutil command: " << dmgCmd.str().c_str() << std::endl;
+    std::cerr << "Please check " << tmpFile.c_str() << " for errors" << std::endl;
+    return 0;
+    }
+
+  return 1;
+}
+
+//----------------------------------------------------------------------
+int cmCPackPackageMakerGenerator::Initialize(const char* name)
+{
+  std::cout << "cmCPackPackageMakerGenerator::Initialize()" << std::endl;
+  int res = this->Superclass::Initialize(name);
+  std::vector<std::string> path;
+  std::string pkgPath = "/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS";
+  path.push_back(pkgPath);
+  pkgPath = cmSystemTools::FindProgram("PackageMaker", path, false);
+  if ( pkgPath.empty() )
+    {
+    std::cerr << "Cannot find PackageMaker compiler" << std::endl;
+    return 0;
+    }
+  this->SetOption("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
+  pkgPath = cmSystemTools::FindProgram("hdiutil", path, false);
+  if ( pkgPath.empty() )
+    {
+    std::cerr << "Cannot find hdiutil compiler" << std::endl;
+    return 0;
+    }
+  this->SetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", pkgPath.c_str());
+
+  return res;
+}
+
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name)
+{
+  std::string uname = cmSystemTools::UpperCase(name);
+  std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
+  const char* inFileName = this->GetOption(cpackVar.c_str());
+  if ( !inFileName )
+    {
+    std::cerr << "CPack option: " << cpackVar.c_str() << " not specified. It should point to " << name << ".rtf, " << name << ".html, or " << name << ".txt file" << std::endl;
+    return false;
+    }
+  if ( !cmSystemTools::FileExists(inFileName) )
+    {
+    std::cerr << "Cannot find " << name << " resource file: " << inFileName << std::endl;
+    return false;
+    }
+  std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
+  if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
+    {
+    std::cerr << "Bad file extension specified: " << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." << std::endl;
+    return false;
+    }
+
+  std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  destFileName += "/Resources/";
+  destFileName += name + ext;
+
+
+  std::cout << "Configure file: " << inFileName << " to " << destFileName.c_str() << std::endl;
+  this->ConfigureFile(inFileName, destFileName.c_str());
+  return true;
+}
+
+bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
+{
+  std::string inFName = "CPack.";
+  inFName += name;
+  inFName += ".in";
+  std::string inFileName = this->FindTemplate(inFName.c_str());
+  if ( inFileName.empty() )
+    {
+    std::cerr << "Cannot find input file: " << inFName << std::endl;
+    return false;
+    }
+
+  std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+  destFileName += "/";
+  destFileName += name;
+
+  std::cout << "Configure file: " << inFileName.c_str() << " to " << destFileName.c_str() << std::endl;
+  this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
+  return true;
+}
+

+ 64 - 0
Source/CPack/cmCPackPackageMakerGenerator.h

@@ -0,0 +1,64 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackPackageMakerGenerator_h
+#define cmCPackPackageMakerGenerator_h
+
+
+#include "cmCPackGenericGenerator.h"
+#include "CPack/cmCPackConfigure.h" // for ssize_t
+
+/** \class cmCPackPackageMakerGenerator
+ * \brief A generator for PackageMaker files
+ *
+ * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/packagemaker.1.html
+ */
+class cmCPackPackageMakerGenerator : public cmCPackGenericGenerator
+{
+public:
+  cmCPackTypeMacro(cmCPackPackageMakerGenerator, cmCPackGenericGenerator);
+  /**
+   * Do the actual processing. Subclass has to override it.
+   * Return < 0 if error.
+   */
+  virtual int ProcessGenerator();
+
+  /**
+   * Initialize generator
+   */
+  virtual int Initialize(const char* name);
+
+  /**
+   * Construct generator
+   */
+  cmCPackPackageMakerGenerator();
+  virtual ~cmCPackPackageMakerGenerator();
+
+protected:
+  int CompressFiles(const char* outFileName, const char* toplevel,
+    const std::vector<std::string>& files);
+  virtual const char* GetOutputExtension() { return "dmg"; }
+  virtual const char* GetOutputPostfix() { return "darwin"; }
+  virtual const char* GetInstallPrefix() { return "/usr"; }
+
+  bool CopyCreateResourceFile(const char* name);
+  bool CopyResourcePlistFile(const char* name);
+};
+
+#endif
+
+

+ 73 - 0
Source/CPack/cmCPackSTGZGenerator.cxx

@@ -0,0 +1,73 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCPackSTGZGenerator.h"
+
+#include "cmake.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmSystemTools.h"
+#include "cmMakefile.h"
+
+
+//----------------------------------------------------------------------
+cmCPackSTGZGenerator::cmCPackSTGZGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+cmCPackSTGZGenerator::~cmCPackSTGZGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+int cmCPackSTGZGenerator::ProcessGenerator()
+{
+  return this->Superclass::ProcessGenerator();
+}
+
+//----------------------------------------------------------------------
+int cmCPackSTGZGenerator::Initialize(const char* name)
+{
+  return this->Superclass::Initialize(name);
+}
+
+//----------------------------------------------------------------------
+int cmCPackSTGZGenerator::GenerateHeader(std::ostream* os)
+{
+  *os
+    << "#!/bin/sh" << std::endl
+    << "echo \"" << this->GetOption("ProjectName")
+    << " - self-extracting archive.\"" << std::endl
+    << "echo \"If you want to stop extracting, please press <ctrl-C>.\"" << std::endl
+    << "read line" << std::endl
+    << "echo \"Extracting... Please wait...\"" << std::endl
+    << "echo \"\"" << std::endl
+    << "" << std::endl
+    << "# take the archive portion of this file and pipe it to tar" << std::endl
+    << "# the NUMERIC parameter in this command should be one more" << std::endl
+    << "# than the number of lines in this header file" << std::endl
+    << "tail +18 $0 | gunzip | tar xf -" << std::endl
+    << "" << std::endl
+    << "exit 0" << std::endl
+    << "echo \"\"" << std::endl
+    << "#-----------------------------------------------------------" << std::endl
+    << "#      Start of TAR.GZ file" << std::endl
+    << "#-----------------------------------------------------------" << std::endl;
+  return 1;
+}
+

+ 57 - 0
Source/CPack/cmCPackSTGZGenerator.h

@@ -0,0 +1,57 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackSTGZGenerator_h
+#define cmCPackSTGZGenerator_h
+
+
+#include "cmCPackTGZGenerator.h"
+
+/** \class cmCPackSTGZGenerator
+ * \brief A generator for Self extractable TGZ files
+ *
+ */
+class cmCPackSTGZGenerator : public cmCPackTGZGenerator
+{
+public:
+  cmCPackTypeMacro(cmCPackSTGZGenerator, cmCPackTGZGenerator);
+  /**
+   * Do the actual processing. Subclass has to override it.
+   * Return < 0 if error.
+   */
+  virtual int ProcessGenerator();
+
+  /**
+   * Initialize generator
+   */
+  virtual int Initialize(const char* name);
+
+  /**
+   * Construct generator
+   */
+  cmCPackSTGZGenerator();
+  virtual ~cmCPackSTGZGenerator();
+
+protected:
+  int GenerateHeader(std::ostream* os);
+  virtual const char* GetOutputExtension() { return "sh"; }
+};
+
+#endif
+
+
+

+ 4067 - 0
Source/CPack/cmCPackTGZGenerator.cxx

@@ -0,0 +1,4067 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCPackTGZGenerator.h"
+
+#include "cmake.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmSystemTools.h"
+#include "cmMakefile.h"
+#include "cmGeneratedFileStream.h"
+
+#include <cmsys/SystemTools.hxx>
+#include <cmzlib/zlib.h>
+
+#include <memory> // auto_ptr
+#include <fcntl.h> // O_WRONLY, O_CREAT
+
+//----------------------------------------------------------------------
+cmCPackTGZGenerator::cmCPackTGZGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+cmCPackTGZGenerator::~cmCPackTGZGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+int cmCPackTGZGenerator::ProcessGenerator()
+{
+  return this->Superclass::ProcessGenerator();
+}
+
+//----------------------------------------------------------------------
+int cmCPackTGZGenerator::Initialize(const char* name)
+{
+  return this->Superclass::Initialize(name);
+}
+
+//----------------------------------------------------------------------
+// The following code is modified version of bsdtar
+/*-
+* Copyright (c) 2003-2004 Tim Kientzle
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer
+*    in this position and unchanged.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+    * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  */
+
+#if defined(HAVE_IO_H)
+#  include <io.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+#  include <unistd.h> // for geteuid
+#endif
+#if defined(HAVE_INTTYPES_H)
+#  include <inttypes.h> // for uintptr_t
+#endif
+#include <sys/types.h>
+#include <errno.h> // for ENOMEM
+#include <sys/stat.h> // for struct stat
+#include <time.h> // for time
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+#  ifndef S_ISREG
+#    define S_ISREG(x) ((x) & _S_IFREG)
+#  endif
+#  ifndef S_ISLNK
+#    define S_ISLNK(x) (0)
+#  endif
+#endif
+
+  //--- archive_plarform.h
+  //
+  /* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+  /* Select the best way to set/get hi-res timestamps. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+  /* FreeBSD uses "timespec" members. */
+#define ARCHIVE_STAT_ATIME_NANOS(st) (st)->st_atimespec.tv_nsec
+#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec
+#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec
+#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n)
+#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n)
+#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n)
+#else
+#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+  /* Linux uses "tim" members. */
+#define ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec
+#define ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec
+#define ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec
+#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n)
+#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n)
+#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n)
+#else
+  /* If we can't find a better way, just use stubs. */
+#define ARCHIVE_STAT_ATIME_NANOS(pstat) 0
+#define ARCHIVE_STAT_CTIME_NANOS(pstat) 0
+#define ARCHIVE_STAT_MTIME_NANOS(pstat) 0
+#define ARCHIVE_STAT_SET_ATIME_NANOS(st, n)
+#define ARCHIVE_STAT_SET_CTIME_NANOS(st, n)
+#define ARCHIVE_STAT_SET_MTIME_NANOS(st, n)
+#endif
+#endif
+
+  //--- archive.h
+
+#define ARCHIVE_BYTES_PER_RECORD   512
+#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240
+
+  /* Declare our basic types. */
+  struct archive;
+  struct archive_entry;
+
+  /*
+   * Error codes: Use archive_errno() and archive_error_string()
+   * to retrieve details.  Unless specified otherwise, all functions
+   * that return 'int' use these codes.
+   */
+#define ARCHIVE_EOF   1 /* Found end of archive. */
+#define ARCHIVE_OK   0 /* Operation was successful. */
+#define ARCHIVE_RETRY (-10) /* Retry might succeed. */
+#define ARCHIVE_WARN (-20) /* Partial sucess. */
+#define ARCHIVE_FATAL (-30) /* No more operations are possible. */
+
+  /*
+   * As far as possible, archive_errno returns standard platform errno codes.
+   * Of course, the details vary by platform, so the actual definitions
+   * here are stored in "archive_platform.h".  The symbols are listed here
+   * for reference; as a rule, clients should not need to know the exact
+   * platform-dependent error code.
+   */
+  /* Unrecognized or invalid file format. */
+  /* #define ARCHIVE_ERRNO_FILE_FORMAT */
+  /* Illegal usage of the library. */
+  /* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */
+  /* Unknown or unclassified error. */
+  /* #define ARCHIVE_ERRNO_MISC */
+
+  /*
+   * Callbacks are invoked to automatically read/write/open/close the archive.
+   * You can provide your own for complex tasks (like breaking archives
+   * across multiple tapes) or use standard ones built into the library.
+   */
+
+  /* Returns pointer and size of next block of data from archive. */
+  typedef ssize_t archive_read_callback(struct archive *, void *_client_data,
+    const void **_buffer);
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef ssize_t archive_write_callback(struct archive *, void *_client_data,
+  void *_buffer, size_t _length);
+typedef int archive_open_callback(struct archive *, void *_client_data);
+typedef int archive_close_callback(struct archive *, void *_client_data);
+
+/*
+ * Codes for archive_compression.
+ */
+#define ARCHIVE_COMPRESSION_NONE 0
+#define ARCHIVE_COMPRESSION_GZIP 1
+#define ARCHIVE_COMPRESSION_BZIP2 2
+#define ARCHIVE_COMPRESSION_COMPRESS 3
+
+/*
+ * Codes returned by archive_format.
+ *
+ * Top 16 bits identifies the format family (e.g., "tar"); lower
+ * 16 bits indicate the variant.  This is updated by read_next_header.
+ * Note that the lower 16 bits will often vary from entry to entry.
+ */
+#define ARCHIVE_FORMAT_BASE_MASK  0xff0000U
+#define ARCHIVE_FORMAT_CPIO   0x10000
+#define ARCHIVE_FORMAT_CPIO_POSIX  (ARCHIVE_FORMAT_CPIO | 1)
+#define ARCHIVE_FORMAT_SHAR   0x20000
+#define ARCHIVE_FORMAT_SHAR_BASE  (ARCHIVE_FORMAT_SHAR | 1)
+#define ARCHIVE_FORMAT_SHAR_DUMP  (ARCHIVE_FORMAT_SHAR | 2)
+#define ARCHIVE_FORMAT_TAR   0x30000
+#define ARCHIVE_FORMAT_TAR_USTAR  (ARCHIVE_FORMAT_TAR | 1)
+#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2)
+#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3)
+#define ARCHIVE_FORMAT_TAR_GNUTAR  (ARCHIVE_FORMAT_TAR | 4)
+#define ARCHIVE_FORMAT_ISO9660   0x40000
+#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1)
+#define ARCHIVE_FORMAT_ZIP   0x50000
+
+/*-
+ * Basic outline for reading an archive:
+ *   1) Ask archive_read_new for an archive reader object.
+ *   2) Update any global properties as appropriate.
+ *      In particular, you'll certainly want to call appropriate
+ *      archive_read_support_XXX functions.
+ *   3) Call archive_read_open_XXX to open the archive
+ *   4) Repeatedly call archive_read_next_header to get information about
+ *      successive archive entries.  Call archive_read_data to extract
+ *      data for entries of interest.
+ *   5) Call archive_read_finish to end processing.
+ */
+struct archive *archive_read_new(void);
+
+/*
+ * The archive_read_support_XXX calls enable auto-detect for this
+ * archive handle.  They also link in the necessary support code.
+ * For example, if you don't want bzlib linked in, don't invoke
+ * support_compression_bzip2().  The "all" functions provide the
+ * obvious shorthand.
+ */
+int   archive_read_support_compression_all(struct archive *);
+int   archive_read_support_compression_bzip2(struct archive *);
+int   archive_read_support_compression_compress(struct archive *);
+int   archive_read_support_compression_gzip(struct archive *);
+int   archive_read_support_compression_none(struct archive *);
+
+int   archive_read_support_format_all(struct archive *);
+int   archive_read_support_format_cpio(struct archive *);
+int   archive_read_support_format_gnutar(struct archive *);
+int   archive_read_support_format_iso9660(struct archive *);
+int   archive_read_support_format_tar(struct archive *);
+int   archive_read_support_format_tp(struct archive *a);
+int   archive_read_support_format_zip(struct archive *);
+
+
+/* Open the archive using callbacks for archive I/O. */
+int   archive_read_open(struct archive *, void *_client_data,
+  archive_open_callback *, archive_read_callback *,
+  archive_close_callback *);
+
+/*
+ * The archive_read_open_file function is a convenience function built
+ * on archive_read_open that uses a canned callback suitable for
+ * common situations.  Note that a NULL filename indicates stdin.
+ */
+int   archive_read_open_file(struct archive *, const char *_file,
+  size_t _block_size);
+int   archive_read_open_fd(struct archive *, int _fd,
+  size_t _block_size);
+
+/* Parses and returns next entry header. */
+int   archive_read_next_header(struct archive *,
+  struct archive_entry **);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+int64_t   archive_read_header_position(struct archive *);
+
+/* Read data from the body of an entry.  Similar to read(2). */
+ssize_t   archive_read_data(struct archive *, void *, size_t);
+/*
+ * A zero-copy version of archive_read_data that also exposes the file offset
+ * of each returned block.  Note that the client has no way to specify
+ * the desired size of the block.  The API does gaurantee that offsets will
+ * be strictly increasing and that returned blocks will not overlap.
+ */
+int   archive_read_data_block(struct archive *a,
+  const void **buff, size_t *size, off_t *offset);
+
+/*-
+ * Some convenience functions that are built on archive_read_data:
+ *  'skip': skips entire entry
+ *  'into_buffer': writes data into memory buffer that you provide
+ *  'into_fd': writes data to specified filedes
+ */
+int   archive_read_data_skip(struct archive *);
+int   archive_read_data_into_buffer(struct archive *, void *buffer,
+  ssize_t len);
+int   archive_read_data_into_fd(struct archive *, int fd);
+
+/*-
+ * Convenience function to recreate the current entry (whose header
+ * has just been read) on disk.
+ *
+ * This does quite a bit more than just copy data to disk. It also:
+ *  - Creates intermediate directories as required.
+ *  - Manages directory permissions:  non-writable directories will
+ *    be initially created with write permission enabled; when the
+ *    archive is closed, dir permissions are edited to the values specified
+ *    in the archive.
+ *  - Checks hardlinks:  hardlinks will not be extracted unless the
+ *    linked-to file was also extracted within the same session. (TODO)
+ */
+
+/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
+/* TODO: The 'Default' comments here are not quite correct; clean this up. */
+#define ARCHIVE_EXTRACT_OWNER (1) /* Default: owner/group not restored */
+#define ARCHIVE_EXTRACT_PERM (2) /* Default: restore perm only for reg file*/
+#define ARCHIVE_EXTRACT_TIME (4) /* Default: mod time not restored */
+#define ARCHIVE_EXTRACT_NO_OVERWRITE (8) /* Default: Replace files on disk */
+#define ARCHIVE_EXTRACT_UNLINK (16) /* Default: don't unlink existing files */
+#define ARCHIVE_EXTRACT_ACL (32) /* Default: don't restore ACLs */
+#define ARCHIVE_EXTRACT_FFLAGS (64) /* Default: don't restore fflags */
+
+int   archive_read_extract(struct archive *, struct archive_entry *,
+  int flags);
+void   archive_read_extract_set_progress_callback(struct archive *,
+  void (*_progress_func)(void *), void *_user_data);
+
+/* Close the file and release most resources. */
+int   archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_finish will call archive_read_close for you. */
+void   archive_read_finish(struct archive *);
+
+/*-
+ * To create an archive:
+ *   1) Ask archive_write_new for a archive writer object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) Call archive_write_open to open the file (most people
+ *       will use archive_write_open_file or archive_write_open_fd,
+ *       which provide convenient canned I/O callbacks for you).
+ *   4) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to write the header
+ *      - archive_write_data to write the entry data
+ *   5) archive_write_close to close the output
+ *   6) archive_write_finish to cleanup the writer and release resources
+ */
+struct archive *archive_write_new(void);
+int   archive_write_set_bytes_per_block(struct archive *,
+  int bytes_per_block);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+int   archive_write_set_bytes_in_last_block(struct archive *,
+  int bytes_in_last_block);
+
+int   archive_write_set_compression_bzip2(struct archive *);
+int   archive_write_set_compression_gzip(struct archive *);
+int   archive_write_set_compression_none(struct archive *);
+/* A convenience function to set the format based on the code or name. */
+int   archive_write_set_format(struct archive *, int format_code);
+int   archive_write_set_format_by_name(struct archive *,
+  const char *name);
+/* To minimize link pollution, use one or more of the following. */
+int   archive_write_set_format_cpio(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+int   archive_write_set_format_pax(struct archive *);
+int   archive_write_set_format_pax_restricted(struct archive *);
+int   archive_write_set_format_shar(struct archive *);
+int   archive_write_set_format_shar_dump(struct archive *);
+int   archive_write_set_format_ustar(struct archive *);
+int   archive_write_open(struct archive *, void *,
+  archive_open_callback *, archive_write_callback *,
+  archive_close_callback *);
+int   archive_write_open_fd(struct archive *, int _fd);
+int   archive_write_open_file(struct archive *, const char *_file);
+
+/*
+ * Note that the library will truncate writes beyond the size provided
+ * to archive_write_header or pad if the provided data is short.
+ */
+int   archive_write_header(struct archive *,
+  struct archive_entry *);
+/* TODO: should be ssize_t, but that might require .so version bump? */
+int   archive_write_data(struct archive *, const void *, size_t);
+int   archive_write_close(struct archive *);
+void   archive_write_finish(struct archive *);
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+/* Bytes written after compression or read before decompression. */
+int64_t   archive_position_compressed(struct archive *);
+/* Bytes written to compressor or read from decompressor. */
+int64_t   archive_position_uncompressed(struct archive *);
+
+const char *archive_compression_name(struct archive *);
+int   archive_compression(struct archive *);
+int   archive_errno(struct archive *);
+const char *archive_error_string(struct archive *);
+const char *archive_format_name(struct archive *);
+int   archive_format(struct archive *);
+void   archive_set_error(struct archive *, int _err, const char *fmt, ...);
+
+//--- archive_string.h
+/*
+ * Basic resizable/reusable string support a la Java's "StringBuffer."
+ *
+ * Unlike sbuf(9), the buffers here are fully reusable and track the
+ * length throughout.
+ *
+ * Note that all visible symbols here begin with "__archive" as they
+ * are internal symbols not intended for anyone outside of this library
+ * to see or use.
+ */
+
+struct archive_string {
+  char *s;  /* Pointer to the storage */
+  size_t  length; /* Length of 's' */
+  size_t  buffer_length; /* Length of malloc-ed storage */
+};
+
+/* Initialize an archive_string object on the stack or elsewhere. */
+#define archive_string_init(a) \
+  do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)
+
+/* Append a C char to an archive_string, resizing as necessary. */
+struct archive_string *
+__archive_strappend_char(struct archive_string *, char);
+#define archive_strappend_char __archive_strappend_char
+
+/* Append a char to an archive_string using UTF8. */
+struct archive_string *
+__archive_strappend_char_UTF8(struct archive_string *, int);
+#define archive_strappend_char_UTF8 __archive_strappend_char_UTF8
+
+/* Append an integer in the specified base (2 <= base <= 16). */
+struct archive_string *
+__archive_strappend_int(struct archive_string *as, int d, int base);
+#define archive_strappend_int __archive_strappend_int
+
+/* Basic append operation. */
+struct archive_string *
+__archive_string_append(struct archive_string *as, const char *p, size_t s);
+
+/* Ensure that the underlying buffer is at least as large as the request. */
+struct archive_string *
+__archive_string_ensure(struct archive_string *, size_t);
+#define archive_string_ensure __archive_string_ensure
+
+/* Append C string, which may lack trailing \0. */
+struct archive_string *
+__archive_strncat(struct archive_string *, const char *, size_t);
+#define archive_strncat  __archive_strncat
+
+/* Append a C string to an archive_string, resizing as necessary. */
+#define archive_strcat(as,p) __archive_string_append((as),(p),strlen(p))
+
+/* Copy a C string to an archive_string, resizing as necessary. */
+#define archive_strcpy(as,p) \
+  ((as)->length = 0, __archive_string_append((as), (p), strlen(p)))
+
+/* Copy a C string to an archive_string with limit, resizing as necessary. */
+#define archive_strncpy(as,p,l) \
+  ((as)->length=0, archive_strncat((as), (p), (l)))
+
+/* Return length of string. */
+#define archive_strlen(a) ((a)->length)
+
+/* Set string length to zero. */
+#define archive_string_empty(a) ((a)->length = 0)
+
+/* Release any allocated storage resources. */
+void __archive_string_free(struct archive_string *);
+#define archive_string_free  __archive_string_free
+
+/* Like 'vsprintf', but resizes the underlying string as necessary. */
+void __archive_string_vsprintf(struct archive_string *, const char *,
+  va_list);
+#define archive_string_vsprintf __archive_string_vsprintf
+//--- archive_private.h
+
+#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU)
+#define ARCHIVE_READ_MAGIC (0xdeb0c5U)
+
+struct archive {
+  /*
+   * The magic/state values are used to sanity-check the
+   * client's usage.  If an API function is called at a
+   * rediculous time, or the client passes us an invalid
+   * pointer, these values allow me to catch that.
+   */
+  unsigned   magic;
+  unsigned   state;
+
+  struct archive_entry *entry;
+  uid_t    user_uid; /* UID of current user. */
+
+  /* Dev/ino of the archive being read/written. */
+  dev_t    skip_file_dev;
+  ino_t    skip_file_ino;
+
+  /* Utility:  Pointer to a block of nulls. */
+  const unsigned char *nulls;
+  size_t    null_length;
+
+  /*
+   * Used by archive_read_data() to track blocks and copy
+   * data to client buffers, filling gaps with zero bytes.
+   */
+  const char  *read_data_block;
+  off_t    read_data_offset;
+  off_t    read_data_output_offset;
+  size_t    read_data_remaining;
+
+  /* Callbacks to open/read/write/close archive stream. */
+  archive_open_callback *client_opener;
+  archive_read_callback *client_reader;
+  archive_write_callback *client_writer;
+  archive_close_callback *client_closer;
+  void   *client_data;
+
+  /*
+   * Blocking information.  Note that bytes_in_last_block is
+   * misleadingly named; I should find a better name.  These
+   * control the final output from all compressors, including
+   * compression_none.
+   */
+  int    bytes_per_block;
+  int    bytes_in_last_block;
+
+  /*
+   * These control whether data within a gzip/bzip2 compressed
+   * stream gets padded or not.  If pad_uncompressed is set,
+   * the data will be padded to a full block before being
+   * compressed.  The pad_uncompressed_byte determines the value
+   * that will be used for padding.  Note that these have no
+   * effect on compression "none."
+   */
+  int    pad_uncompressed;
+  int    pad_uncompressed_byte; /* TODO: Support this. */
+
+  /* Position in UNCOMPRESSED data stream. */
+  off_t    file_position;
+  /* Position in COMPRESSED data stream. */
+  off_t    raw_position;
+  /* File offset of beginning of most recently-read header. */
+  off_t    header_position;
+
+  /*
+   * Detection functions for decompression: bid functions are
+   * given a block of data from the beginning of the stream and
+   * can bid on whether or not they support the data stream.
+   * General guideline: bid the number of bits that you actually
+   * test, e.g., 16 if you test a 2-byte magic value.  The
+   * highest bidder will have their init function invoked, which
+   * can set up pointers to specific handlers.
+   *
+   * On write, the client just invokes an archive_write_set function
+   * which sets up the data here directly.
+   */
+  int   compression_code; /* Currently active compression. */
+  const char *compression_name;
+  struct {
+    int (*bid)(const void *buff, size_t);
+    int (*init)(struct archive *, const void *buff, size_t);
+  } decompressors[4];
+  /* Read/write data stream (with compression). */
+  void  *compression_data;  /* Data for (de)compressor. */
+  int (*compression_init)(struct archive *); /* Initialize. */
+  int (*compression_finish)(struct archive *);
+  int (*compression_write)(struct archive *, const void *, size_t);
+  /*
+   * Read uses a peek/consume I/O model: the decompression code
+   * returns a pointer to the requested block and advances the
+   * file position only when requested by a consume call.  This
+   * reduces copying and also simplifies look-ahead for format
+   * detection.
+   */
+  ssize_t (*compression_read_ahead)(struct archive *,
+    const void **, size_t request);
+  ssize_t (*compression_read_consume)(struct archive *, size_t);
+
+  /*
+   * Format detection is mostly the same as compression
+   * detection, with two significant differences: The bidders
+   * use the read_ahead calls above to examine the stream rather
+   * than having the supervisor hand them a block of data to
+   * examine, and the auction is repeated for every header.
+   * Winning bidders should set the archive_format and
+   * archive_format_name appropriately.  Bid routines should
+   * check archive_format and decline to bid if the format of
+   * the last header was incompatible.
+   *
+   * Again, write support is considerably simpler because there's
+   * no need for an auction.
+   */
+  int    archive_format;
+  const char  *archive_format_name;
+
+  struct archive_format_descriptor {
+    int (*bid)(struct archive *);
+    int (*read_header)(struct archive *, struct archive_entry *);
+    int (*read_data)(struct archive *, const void **, size_t *, off_t *);
+    int (*read_data_skip)(struct archive *);
+    int (*cleanup)(struct archive *);
+    void  *format_data; /* Format-specific data for readers. */
+  } formats[8];
+  struct archive_format_descriptor *format; /* Active format. */
+
+  /*
+   * Storage for format-specific data.  Note that there can be
+   * multiple format readers active at one time, so we need to
+   * allow for multiple format readers to have their data
+   * available.  The pformat_data slot here is the solution: on
+   * read, it is gauranteed to always point to a void* variable
+   * that the format can use.
+   */
+  void **pformat_data;  /* Pointer to current format_data. */
+  void  *format_data;  /* Used by writers. */
+
+  /*
+   * Pointers to format-specific functions for writing.  They're
+   * initialized by archive_write_set_format_XXX() calls.
+   */
+  int (*format_init)(struct archive *); /* Only used on write. */
+  int (*format_finish)(struct archive *);
+  int (*format_finish_entry)(struct archive *);
+  int  (*format_write_header)(struct archive *,
+    struct archive_entry *);
+  int (*format_write_data)(struct archive *,
+    const void *buff, size_t);
+
+  /*
+   * Various information needed by archive_extract.
+   */
+  struct extract   *extract;
+  void   (*extract_progress)(void *);
+  void    *extract_progress_user_data;
+  void   (*cleanup_archive_extract)(struct archive *);
+
+  int    archive_error_number;
+  const char  *error;
+  struct archive_string error_string;
+};
+
+
+#define ARCHIVE_STATE_ANY 0xFFFFU
+#define ARCHIVE_STATE_NEW 1U
+#define ARCHIVE_STATE_HEADER 2U
+#define ARCHIVE_STATE_DATA 4U
+#define ARCHIVE_STATE_EOF 8U
+#define ARCHIVE_STATE_CLOSED 0x10U
+#define ARCHIVE_STATE_FATAL 0x8000U
+
+/* Check magic value and state; exit if it isn't valid. */
+void __archive_check_magic(struct archive *, unsigned magic,
+  unsigned state, const char *func);
+
+
+int __archive_read_register_format(struct archive *a,
+  void *format_data,
+  int (*bid)(struct archive *),
+  int (*read_header)(struct archive *, struct archive_entry *),
+  int (*read_data)(struct archive *, const void **, size_t *, off_t *),
+  int (*read_data_skip)(struct archive *),
+  int (*cleanup)(struct archive *));
+
+int __archive_read_register_compression(struct archive *a,
+  int (*bid)(const void *, size_t),
+  int (*init)(struct archive *, const void *, size_t));
+
+void __archive_errx(int retvalue, const char *msg);
+
+#define err_combine(a,b) ((a) < (b) ? (a) : (b))
+
+
+                                             /*
+                                              * Utility function to format a USTAR header into a buffer.  If
+                                              * "strict" is set, this tries to create the absolutely most portable
+                                              * version of a ustar header.  If "strict" is set to 0, then it will
+                                              * relax certain requirements.
+                                              *
+                                              * Generally, format-specific declarations don't belong in this
+                                              * header; this is a rare example of a function that is shared by
+                                              * two very similar formats (ustar and pax).
+                                              */
+                                             int
+                                             __archive_write_format_header_ustar(struct archive *, char buff[512],
+                                               struct archive_entry *, int tartype, int strict);
+
+//--- archive_entry.h
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries that are
+ * supported by "pax interchange" format.  However, GNU, ustar, cpio,
+ * and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).
+ */
+struct archive_entry;
+
+/*
+ * Basic object manipulation
+ */
+
+struct archive_entry *archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+struct archive_entry *archive_entry_clone(struct archive_entry *);
+void    archive_entry_free(struct archive_entry *);
+struct archive_entry *archive_entry_new(void);
+
+/*
+ * Retrieve fields from an archive_entry.
+ */
+
+time_t    archive_entry_atime(struct archive_entry *);
+long    archive_entry_atime_nsec(struct archive_entry *);
+time_t    archive_entry_ctime(struct archive_entry *);
+long    archive_entry_ctime_nsec(struct archive_entry *);
+dev_t    archive_entry_dev(struct archive_entry *);
+void    archive_entry_fflags(struct archive_entry *,
+  unsigned long *set, unsigned long *clear);
+const char  *archive_entry_fflags_text(struct archive_entry *);
+gid_t    archive_entry_gid(struct archive_entry *);
+const char  *archive_entry_gname(struct archive_entry *);
+const wchar_t  *archive_entry_gname_w(struct archive_entry *);
+const char  *archive_entry_hardlink(struct archive_entry *);
+const wchar_t  *archive_entry_hardlink_w(struct archive_entry *);
+ino_t    archive_entry_ino(struct archive_entry *);
+mode_t    archive_entry_mode(struct archive_entry *);
+time_t    archive_entry_mtime(struct archive_entry *);
+long    archive_entry_mtime_nsec(struct archive_entry *);
+const char  *archive_entry_pathname(struct archive_entry *);
+const wchar_t  *archive_entry_pathname_w(struct archive_entry *);
+dev_t    archive_entry_rdev(struct archive_entry *);
+int64_t    archive_entry_size(struct archive_entry *);
+const struct stat *archive_entry_stat(struct archive_entry *);
+const char  *archive_entry_symlink(struct archive_entry *);
+const wchar_t  *archive_entry_symlink_w(struct archive_entry *);
+uid_t    archive_entry_uid(struct archive_entry *);
+const char  *archive_entry_uname(struct archive_entry *);
+const wchar_t  *archive_entry_uname_w(struct archive_entry *);
+
+/*
+ * Set fields in an archive_entry.
+ *
+ * Note that string 'set' functions do not copy the string, only the pointer.
+ * In contrast, 'copy' functions do copy the object pointed to.
+ */
+
+void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+void archive_entry_set_atime(struct archive_entry *, time_t, long);
+void archive_entry_set_ctime(struct archive_entry *, time_t, long);
+void archive_entry_set_fflags(struct archive_entry *,
+  unsigned long set, unsigned long clear);
+/* Returns pointer to start of first invalid token, or NULL if none. */
+/* Note that all recognized tokens are processed, regardless. */
+const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+  const wchar_t *);
+void archive_entry_set_gid(struct archive_entry *, gid_t);
+void archive_entry_set_gname(struct archive_entry *, const char *);
+void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+void archive_entry_set_hardlink(struct archive_entry *, const char *);
+void archive_entry_copy_hardlink(struct archive_entry *, const char *);
+void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+void archive_entry_set_link(struct archive_entry *, const char *);
+void archive_entry_set_mode(struct archive_entry *, mode_t);
+void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+void archive_entry_set_pathname(struct archive_entry *, const char *);
+void archive_entry_copy_pathname(struct archive_entry *, const char *);
+void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+void archive_entry_set_size(struct archive_entry *, int64_t);
+void archive_entry_set_symlink(struct archive_entry *, const char *);
+void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+void archive_entry_set_uid(struct archive_entry *, uid_t);
+void archive_entry_set_uname(struct archive_entry *, const char *);
+void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+
+/*
+ * ACL routines.  This used to simply store and return text-format ACL
+ * strings, but that proved insufficient for a number of reasons:
+ *   = clients need control over uname/uid and gname/gid mappings
+ *   = there are many different ACL text formats
+ *   = would like to be able to read/convert archives containing ACLs
+ *     on platforms that lack ACL libraries
+ */
+
+/*
+ * Permission bits mimic POSIX.1e.  Note that I've not followed POSIX.1e's
+ * "permset"/"perm" abstract type nonsense.  A permset is just a simple
+ * bitmap, following long-standing Unix tradition.
+ */
+#define ARCHIVE_ENTRY_ACL_EXECUTE 1
+#define ARCHIVE_ENTRY_ACL_WRITE  2
+#define ARCHIVE_ENTRY_ACL_READ  4
+
+/* We need to be able to specify either or both of these. */
+#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256
+#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512
+
+/* Tag values mimic POSIX.1e */
+#define ARCHIVE_ENTRY_ACL_USER  10001 /* Specified user. */
+#define ARCHIVE_ENTRY_ACL_USER_OBJ  10002 /* User who owns the file. */
+#define ARCHIVE_ENTRY_ACL_GROUP  10003 /* Specified group. */
+#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */
+#define ARCHIVE_ENTRY_ACL_MASK  10005 /* Modify group access. */
+#define ARCHIVE_ENTRY_ACL_OTHER  10006 /* Public. */
+
+/*
+ * Set the ACL by clearing it and adding entries one at a time.
+ * Unlike the POSIX.1e ACL routines, you must specify the type
+ * (access/default) for each entry.  Internally, the ACL data is just
+ * a soup of entries.  API calls here allow you to retrieve just the
+ * entries of interest.  This design (which goes against the spirit of
+ * POSIX.1e) is useful for handling archive formats that combine
+ * default and access information in a single ACL list.
+ */
+void  archive_entry_acl_clear(struct archive_entry *);
+void  archive_entry_acl_add_entry(struct archive_entry *,
+  int type, int permset, int tag, int qual, const char *name);
+void  archive_entry_acl_add_entry_w(struct archive_entry *,
+  int type, int permset, int tag, int qual, const wchar_t *name);
+
+/*
+ * To retrieve the ACL, first "reset", then repeatedly ask for the
+ * "next" entry.  The want_type parameter allows you to request only
+ * access entries or only default entries.
+ */
+int  archive_entry_acl_reset(struct archive_entry *, int want_type);
+int  archive_entry_acl_next(struct archive_entry *, int want_type,
+  int *type, int *permset, int *tag, int *qual, const char **name);
+int  archive_entry_acl_next_w(struct archive_entry *, int want_type,
+  int *type, int *permset, int *tag, int *qual,
+  const wchar_t **name);
+
+/*
+ * Construct a text-format ACL.  The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.  (As used by 'star'.)
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ *    default ACL entry.
+ */
+#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024
+#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048
+const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int flags);
+
+/* Return a count of entries matching 'want_type' */
+int  archive_entry_acl_count(struct archive_entry *, int want_type);
+
+/*
+ * Private ACL parser.  This is private because it handles some
+ * very weird formats that clients should not be messing with.
+ * Clients should only deal with their platform-native formats.
+ * Because of the need to support many formats cleanly, new arguments
+ * are likely to get added on a regular basis.  Clients who try to use
+ * this interface are likely to be surprised when it changes.
+ *
+ * You were warned!
+ */
+int   __archive_entry_acl_parse_w(struct archive_entry *,
+  const wchar_t *, int type);
+//--- archive_write.h
+
+/*
+ * Allocate, initialize and return an archive object.
+ */
+  struct archive *
+archive_write_new(void)
+{
+  struct archive *a;
+  unsigned char *nulls;
+
+  a = (struct archive*)malloc(sizeof(*a));
+  if (a == NULL)
+    return (NULL);
+  memset(a, 0, sizeof(*a));
+  a->magic = ARCHIVE_WRITE_MAGIC;
+#ifdef HAVE_GETEUID
+  a->user_uid = geteuid();
+#endif
+  a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
+  a->bytes_in_last_block = -1; /* Default */
+  a->state = ARCHIVE_STATE_NEW;
+  a->pformat_data = &(a->format_data);
+
+  /* Initialize a block of nulls for padding purposes. */
+  a->null_length = 1024;
+  nulls = (unsigned char*)malloc(a->null_length);
+  if (nulls == NULL) {
+    free(a);
+    return (NULL);
+  }
+  memset(nulls, 0, a->null_length);
+  a->nulls = nulls;
+  /*
+   * Set default compression, but don't set a default format.
+   * Were we to set a default format here, we would force every
+   * client to link in support for that format, even if they didn't
+   * ever use it.
+   */
+  archive_write_set_compression_none(a);
+  return (a);
+}
+
+
+/*
+ * Set the block size.  Returns 0 if successful.
+ */
+  int
+archive_write_set_bytes_per_block(struct archive *a, int bytes_per_block)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
+  a->bytes_per_block = bytes_per_block;
+  return (ARCHIVE_OK);
+}
+
+
+/*
+ * Set the size for the last block.
+ * Returns 0 if successful.
+ */
+  int
+archive_write_set_bytes_in_last_block(struct archive *a, int bytes)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
+  a->bytes_in_last_block = bytes;
+  return (ARCHIVE_OK);
+}
+
+
+/*
+ * Open the archive using the current settings.
+ */
+  int
+archive_write_open(struct archive *a, void *client_data,
+  archive_open_callback *opener, archive_write_callback *writer,
+  archive_close_callback *closer)
+{
+  int ret;
+
+  ret = ARCHIVE_OK;
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open");
+  archive_string_empty(&a->error_string);
+  a->state = ARCHIVE_STATE_HEADER;
+  a->client_data = client_data;
+  a->client_writer = writer;
+  a->client_opener = opener;
+  a->client_closer = closer;
+  ret = (a->compression_init)(a);
+  if (a->format_init && ret == ARCHIVE_OK)
+    ret = (a->format_init)(a);
+  return (ret);
+}
+
+
+/*
+ * Close out the archive.
+ *
+ * Be careful: user might just call write_new and then write_finish.
+ * Don't assume we actually wrote anything or performed any non-trivial
+ * initialization.
+ */
+  int
+archive_write_close(struct archive *a)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_close");
+
+  /* Finish the last entry. */
+  if (a->state & ARCHIVE_STATE_DATA)
+    ((a->format_finish_entry)(a));
+
+  /* Finish off the archive. */
+  if (a->format_finish != NULL)
+    (a->format_finish)(a);
+
+  /* Finish the compression and close the stream. */
+  if (a->compression_finish != NULL)
+    (a->compression_finish)(a);
+
+  a->state = ARCHIVE_STATE_CLOSED;
+  return (ARCHIVE_OK);
+}
+
+/*
+ * Destroy the archive structure.
+ */
+  void
+archive_write_finish(struct archive *a)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_finish");
+  if (a->state != ARCHIVE_STATE_CLOSED)
+    archive_write_close(a);
+
+  /* Release various dynamic buffers. */
+  free((void *)(uintptr_t)(const void *)a->nulls);
+  archive_string_free(&a->error_string);
+  a->magic = 0;
+  free(a);
+}
+
+
+/*
+ * Write the appropriate header.
+ */
+  int
+archive_write_header(struct archive *a, struct archive_entry *entry)
+{
+  int ret;
+
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_header");
+  archive_string_empty(&a->error_string);
+
+  /* Finish last entry. */
+  if (a->state & ARCHIVE_STATE_DATA)
+    ((a->format_finish_entry)(a));
+
+  if (archive_entry_dev(entry) == a->skip_file_dev &&
+    archive_entry_ino(entry) == a->skip_file_ino) {
+    archive_set_error(a, 0, "Can't add archive to itself");
+    return (ARCHIVE_WARN);
+  }
+
+  /* Format and write header. */
+  ret = ((a->format_write_header)(a, entry));
+
+  a->state = ARCHIVE_STATE_DATA;
+  return (ret);
+}
+
+/*
+ * Note that the compressor is responsible for blocking.
+ */
+/* Should be "ssize_t", but that breaks the ABI.  <sigh> */
+  int
+archive_write_data(struct archive *a, const void *buff, size_t s)
+{
+  int ret;
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data");
+  archive_string_empty(&a->error_string);
+  ret = (a->format_write_data)(a, buff, s);
+  return (ret == ARCHIVE_OK ? (ssize_t)s : -1);
+}
+
+//--- archive_write_set_compression_none.c
+
+static int archive_compressor_none_finish(struct archive *a);
+static int archive_compressor_none_init(struct archive *);
+static int archive_compressor_none_write(struct archive *, const void *,
+  size_t);
+
+struct archive_none {
+  char *buffer;
+  ssize_t  buffer_size;
+  char *next;  /* Current insert location */
+  ssize_t  avail;  /* Free space left in buffer */
+};
+
+  int
+archive_write_set_compression_none(struct archive *a)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
+  a->compression_init = &archive_compressor_none_init;
+  a->compression_code = ARCHIVE_COMPRESSION_NONE;
+  a->compression_name = "none";
+  return (0);
+}
+
+/*
+ * Setup callback.
+ */
+  static int
+archive_compressor_none_init(struct archive *a)
+{
+  int ret;
+  struct archive_none *state;
+
+  a->compression_code = ARCHIVE_COMPRESSION_NONE;
+  a->compression_name = "none";
+
+  if (a->client_opener != NULL) {
+    ret = (a->client_opener)(a, a->client_data);
+    if (ret != 0)
+      return (ret);
+  }
+
+  state = (struct archive_none *)malloc(sizeof(*state));
+  if (state == NULL) {
+    archive_set_error(a, ENOMEM,
+      "Can't allocate data for output buffering");
+    return (ARCHIVE_FATAL);
+  }
+  memset(state, 0, sizeof(*state));
+
+  state->buffer_size = a->bytes_per_block;
+  state->buffer = (char*)malloc(state->buffer_size);
+
+  if (state->buffer == NULL) {
+    archive_set_error(a, ENOMEM,
+      "Can't allocate output buffer");
+    free(state);
+    return (ARCHIVE_FATAL);
+  }
+
+  state->next = state->buffer;
+  state->avail = state->buffer_size;
+
+  a->compression_data = state;
+  a->compression_write = archive_compressor_none_write;
+  a->compression_finish = archive_compressor_none_finish;
+  return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the stream.
+ */
+  static int
+archive_compressor_none_write(struct archive *a, const void *vbuff,
+  size_t length)
+{
+  const char *buff;
+  ssize_t remaining, to_copy;
+  ssize_t bytes_written;
+  struct archive_none *state;
+
+  state = (struct archive_none*)a->compression_data;
+  buff = (const char*)vbuff;
+  if (a->client_writer == NULL) {
+    archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+      "No write callback is registered?  "
+      "This is probably an internal programming error.");
+    return (ARCHIVE_FATAL);
+  }
+
+  remaining = length;
+  while (remaining > 0) {
+    /*
+     * If we have a full output block, write it and reset the
+     * output buffer.
+     */
+    if (state->avail == 0) {
+      bytes_written = (a->client_writer)(a, a->client_data,
+        state->buffer, state->buffer_size);
+      if (bytes_written <= 0)
+        return (ARCHIVE_FATAL);
+      /* XXX TODO: if bytes_written < state->buffer_size */
+      a->raw_position += bytes_written;
+      state->next = state->buffer;
+      state->avail = state->buffer_size;
+    }
+
+    /* Now we have space in the buffer; copy new data into it. */
+    to_copy = (remaining > state->avail) ?
+      state->avail : remaining;
+    memcpy(state->next, buff, to_copy);
+    state->next += to_copy;
+    state->avail -= to_copy;
+    buff += to_copy;
+    remaining -= to_copy;
+  }
+  a->file_position += length;
+  return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression.
+ */
+  static int
+archive_compressor_none_finish(struct archive *a)
+{
+  ssize_t block_length;
+  ssize_t target_block_length;
+  ssize_t bytes_written;
+  int ret;
+  int ret2;
+  struct archive_none *state;
+
+  state = (struct archive_none*)a->compression_data;
+  ret = ret2 = ARCHIVE_OK;
+  if (a->client_writer == NULL) {
+    archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+      "No write callback is registered?  "
+      "This is probably an internal programming error.");
+    return (ARCHIVE_FATAL);
+  }
+
+  /* If there's pending data, pad and write the last block */
+  if (state->next != state->buffer) {
+    block_length = state->buffer_size - state->avail;
+
+    /* Tricky calculation to determine size of last block */
+    target_block_length = block_length;
+    if (a->bytes_in_last_block <= 0)
+      /* Default or Zero: pad to full block */
+      target_block_length = a->bytes_per_block;
+    else
+      /* Round to next multiple of bytes_in_last_block. */
+      target_block_length = a->bytes_in_last_block *
+        ( (block_length + a->bytes_in_last_block - 1) /
+          a->bytes_in_last_block);
+    if (target_block_length > a->bytes_per_block)
+      target_block_length = a->bytes_per_block;
+    if (block_length < target_block_length) {
+      memset(state->next, 0,
+        target_block_length - block_length);
+      block_length = target_block_length;
+    }
+    bytes_written = (a->client_writer)(a, a->client_data,
+      state->buffer, block_length);
+    if (bytes_written <= 0)
+      ret = ARCHIVE_FATAL;
+    else {
+      a->raw_position += bytes_written;
+      ret = ARCHIVE_OK;
+    }
+  }
+
+  /* Close the output */
+  if (a->client_closer != NULL)
+    ret2 = (a->client_closer)(a, a->client_data);
+
+  free(state->buffer);
+  free(state);
+  a->compression_data = NULL;
+
+  return (ret != ARCHIVE_OK ? ret : ret2);
+}
+
+//--- archive_check_magic.c
+
+  static void
+errmsg(const char *m)
+{
+  cmSystemTools::Error("CPack error: ", m);
+}
+
+  static void
+diediedie(void)
+{
+  *(char *)0 = 1; /* Deliberately segfault and force a coredump. */
+  _exit(1); /* If that didn't work, just exit with an error. */
+}
+
+  static const char *
+state_name(unsigned s)
+{
+  switch (s) {
+  case ARCHIVE_STATE_NEW:  return ("new");
+  case ARCHIVE_STATE_HEADER: return ("header");
+  case ARCHIVE_STATE_DATA: return ("data");
+  case ARCHIVE_STATE_EOF:  return ("eof");
+  case ARCHIVE_STATE_CLOSED: return ("closed");
+  case ARCHIVE_STATE_FATAL: return ("fatal");
+  default:   return ("??");
+  }
+}
+
+
+  static void
+write_all_states(int states)
+{
+  unsigned lowbit;
+
+  /* A trick for computing the lowest set bit. */
+  while ((lowbit = states & (-states)) != 0) {
+    states &= ~lowbit;  /* Clear the low bit. */
+    errmsg(state_name(lowbit));
+    if (states != 0)
+      errmsg("/");
+  }
+}
+
+/*
+ * Check magic value and current state; bail if it isn't valid.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+  void
+__archive_check_magic(struct archive *a, unsigned magic, unsigned state,
+  const char *function)
+{
+  if (a->magic != magic) {
+    errmsg("INTERNAL ERROR: Function ");
+    errmsg(function);
+    errmsg(" invoked with invalid struct archive structure.\n");
+    diediedie();
+  }
+
+  if (state == ARCHIVE_STATE_ANY)
+    return;
+
+  if ((a->state & state) == 0) {
+    errmsg("INTERNAL ERROR: Function '");
+    errmsg(function);
+    errmsg("' invoked with archive structure in state '");
+    write_all_states(a->state);
+    errmsg("', should be in state '");
+    write_all_states(state);
+    errmsg("'\n");
+    diediedie();
+  }
+}
+
+//--- archive_util.c
+  int
+archive_errno(struct archive *a)
+{
+  return (a->archive_error_number);
+}
+
+  const char *
+archive_error_string(struct archive *a)
+{
+
+  if (a->error != NULL  &&  *a->error != '\0')
+    return (a->error);
+  else
+    return ("(Empty error message)");
+}
+
+
+  int
+archive_format(struct archive *a)
+{
+  return (a->archive_format);
+}
+
+  const char *
+archive_format_name(struct archive *a)
+{
+  return (a->archive_format_name);
+}
+
+
+  int
+archive_compression(struct archive *a)
+{
+  return (a->compression_code);
+}
+
+  const char *
+archive_compression_name(struct archive *a)
+{
+  return (a->compression_name);
+}
+
+
+/*
+ * Return a count of the number of compressed bytes processed.
+ */
+  int64_t
+archive_position_compressed(struct archive *a)
+{
+  return (a->raw_position);
+}
+
+/*
+ * Return a count of the number of uncompressed bytes processed.
+ */
+  int64_t
+archive_position_uncompressed(struct archive *a)
+{
+  return (a->file_position);
+}
+
+
+  void
+archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
+{
+  va_list ap;
+#ifdef HAVE_STRERROR_R
+  char errbuff[512];
+#endif
+  char *errp;
+
+  a->archive_error_number = error_number;
+  if (fmt == NULL) {
+    a->error = NULL;
+    return;
+  }
+
+  va_start(ap, fmt);
+  archive_string_vsprintf(&(a->error_string), fmt, ap);
+  if (error_number > 0) {
+    archive_strcat(&(a->error_string), ": ");
+#ifdef HAVE_STRERROR_R
+#ifdef STRERROR_R_CHAR_P
+    errp = strerror_r(error_number, errbuff, sizeof(errbuff));
+#else
+    strerror_r(error_number, errbuff, sizeof(errbuff));
+    errp = errbuff;
+#endif
+#else
+    /* Note: this is not threadsafe! */
+    errp = strerror(error_number);
+#endif
+    archive_strcat(&(a->error_string), errp);
+  }
+  a->error = a->error_string.s;
+  va_end(ap);
+}
+
+  void
+__archive_errx(int retvalue, const char *msg)
+{
+  static const char *msg1 = "Fatal Internal Error in libarchive: ";
+  write(2, msg1, strlen(msg1));
+  write(2, msg, strlen(msg));
+  write(2, "\n", 1);
+  exit(retvalue);
+}
+
+//--- archive_string.c
+  struct archive_string *
+__archive_string_append(struct archive_string *as, const char *p, size_t s)
+{
+  __archive_string_ensure(as, as->length + s + 1);
+  memcpy(as->s + as->length, p, s);
+  as->s[as->length + s] = 0;
+  as->length += s;
+  return (as);
+}
+
+  void
+__archive_string_free(struct archive_string *as)
+{
+  as->length = 0;
+  as->buffer_length = 0;
+  if (as->s != NULL)
+    free(as->s);
+}
+
+  struct archive_string *
+__archive_string_ensure(struct archive_string *as, size_t s)
+{
+  if (as->s && (s <= as->buffer_length))
+    return (as);
+
+  if (as->buffer_length < 32)
+    as->buffer_length = 32;
+  while (as->buffer_length < s)
+    as->buffer_length *= 2;
+  as->s = (char*)realloc(as->s, as->buffer_length);
+  /* TODO: Return null instead and fix up all of our callers to
+   * handle this correctly. */
+  if (as->s == NULL)
+    __archive_errx(1, "Out of memory");
+  return (as);
+}
+
+  struct archive_string *
+__archive_strncat(struct archive_string *as, const char *p, size_t n)
+{
+  size_t s;
+  const char *pp;
+
+  /* Like strlen(p), except won't examine positions beyond p[n]. */
+  s = 0;
+  pp = p;
+  while (*pp && s < n) {
+    pp++;
+    s++;
+  }
+  return (__archive_string_append(as, p, s));
+}
+
+  struct archive_string *
+__archive_strappend_char(struct archive_string *as, char c)
+{
+  return (__archive_string_append(as, &c, 1));
+}
+
+  struct archive_string *
+__archive_strappend_int(struct archive_string *as, int d, int base)
+{
+  static const char *digits = "0123457890abcdef";
+
+  if (d < 0) {
+    __archive_strappend_char(as, '-');
+    d = -d;
+  }
+  if (d >= base)
+    __archive_strappend_int(as, d/base, base);
+  __archive_strappend_char(as, digits[d % base]);
+  return (as);
+}
+
+//--- archive_entry.c
+/* Obtain suitable wide-character manipulation functions. */
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#else
+static size_t wcslen(const wchar_t *s)
+{
+  const wchar_t *p = s;
+  while (*p != L'\0')
+    ++p;
+  return p - s;
+}
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+  wchar_t *dest = s1;
+  while((*s1 = *s2) != L'\0')
+    ++s1, ++s2;
+  return dest;
+}
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a),(b),(i)*sizeof(wchar_t))
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a),(b),(i)*sizeof(wchar_t))
+#endif
+
+#undef max
+#define max(a, b) ((a)>(b)?(a):(b))
+
+                                /*
+                                 * Handle wide character (i.e., Unicode) and non-wide character
+                                 * strings transparently.
+                                 *
+                                 */
+
+                                struct aes {
+                                  const char *aes_mbs;
+                                  char *aes_mbs_alloc;
+                                  const wchar_t *aes_wcs;
+                                  wchar_t *aes_wcs_alloc;
+                                };
+
+struct ae_acl {
+  struct ae_acl *next;
+  int type;   /* E.g., access or default */
+  int tag;   /* E.g., user/group/other/mask */
+  int permset;  /* r/w/x bits */
+  int id;   /* uid/gid for user/group */
+  struct aes name;  /* uname/gname */
+};
+
+static void aes_clean(struct aes *);
+static void aes_copy(struct aes *dest, struct aes *src);
+static const char * aes_get_mbs(struct aes *);
+static const wchar_t * aes_get_wcs(struct aes *);
+static void aes_set_mbs(struct aes *, const char *mbs);
+static void aes_copy_mbs(struct aes *, const char *mbs);
+/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */
+static void aes_copy_wcs(struct aes *, const wchar_t *wcs);
+
+static char *  ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
+static const wchar_t *ae_wcstofflags(const wchar_t *stringp,
+  unsigned long *setp, unsigned long *clrp);
+static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+  const wchar_t *wname, int perm, int id);
+static void append_id_w(wchar_t **wp, int id);
+
+static int acl_special(struct archive_entry *entry,
+  int type, int permset, int tag);
+static struct ae_acl *acl_new_entry(struct archive_entry *entry,
+  int type, int permset, int tag, int id);
+static void next_field_w(const wchar_t **wp, const wchar_t **start,
+  const wchar_t **end, wchar_t *sep);
+static int prefix_w(const wchar_t *start, const wchar_t *end,
+  const wchar_t *test);
+
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format.  However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).  There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+  /*
+   * Note that ae_stat.st_mode & S_IFMT  can be  0!
+   *
+   * This occurs when the actual file type of the object is not
+   * in the archive.  For example, 'tar' archives store
+   * hardlinks without marking the type of the underlying
+   * object.
+   */
+  struct stat ae_stat;
+
+  /*
+   * Use aes here so that we get transparent mbs<->wcs conversions.
+   */
+  struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */
+  unsigned long ae_fflags_set;  /* Bitmap fflags */
+  unsigned long ae_fflags_clear;
+  struct aes ae_gname;  /* Name of owning group */
+  struct aes ae_hardlink; /* Name of target for hardlink */
+  struct aes ae_pathname; /* Name of entry */
+  struct aes ae_symlink;  /* symlink contents */
+  struct aes ae_uname;  /* Name of owner */
+
+  struct ae_acl *acl_head;
+  struct ae_acl *acl_p;
+  int   acl_state; /* See acl_next for details. */
+  wchar_t  *acl_text_w;
+};
+
+  static void
+aes_clean(struct aes *aes)
+{
+  if (aes->aes_mbs_alloc) {
+    free(aes->aes_mbs_alloc);
+    aes->aes_mbs_alloc = NULL;
+  }
+  if (aes->aes_wcs_alloc) {
+    free(aes->aes_wcs_alloc);
+    aes->aes_wcs_alloc = NULL;
+  }
+  memset(aes, 0, sizeof(*aes));
+}
+
+  static void
+aes_copy(struct aes *dest, struct aes *src)
+{
+  *dest = *src;
+  if (src->aes_mbs != NULL) {
+    dest->aes_mbs_alloc = strdup(src->aes_mbs);
+    dest->aes_mbs = dest->aes_mbs_alloc;
+    if (dest->aes_mbs == NULL)
+      __archive_errx(1, "No memory for aes_copy()");
+  }
+
+  if (src->aes_wcs != NULL) {
+    dest->aes_wcs_alloc = (wchar_t*)malloc((wcslen(src->aes_wcs) + 1)
+      * sizeof(wchar_t));
+    dest->aes_wcs = dest->aes_wcs_alloc;
+    if (dest->aes_wcs == NULL)
+      __archive_errx(1, "No memory for aes_copy()");
+    wcscpy(dest->aes_wcs_alloc, src->aes_wcs);
+  }
+}
+
+  static const char *
+aes_get_mbs(struct aes *aes)
+{
+  if (aes->aes_mbs == NULL && aes->aes_wcs == NULL)
+    return NULL;
+  if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) {
+    /*
+     * XXX Need to estimate the number of byte in the
+     * multi-byte form.  Assume that, on average, wcs
+     * chars encode to no more than 3 bytes.  There must
+     * be a better way... XXX
+     */
+    int mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+    aes->aes_mbs_alloc = (char*)malloc(mbs_length);
+    aes->aes_mbs = aes->aes_mbs_alloc;
+    if (aes->aes_mbs == NULL)
+      __archive_errx(1, "No memory for aes_get_mbs()");
+    wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1);
+    aes->aes_mbs_alloc[mbs_length - 1] = 0;
+  }
+  return (aes->aes_mbs);
+}
+
+  static const wchar_t *
+aes_get_wcs(struct aes *aes)
+{
+  if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
+    return NULL;
+  if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
+    /*
+     * No single byte will be more than one wide character,
+     * so this length estimate will always be big enough.
+     */
+    int wcs_length = strlen(aes->aes_mbs);
+    aes->aes_wcs_alloc
+      = (wchar_t*)malloc((wcs_length + 1) * sizeof(wchar_t));
+    aes->aes_wcs = aes->aes_wcs_alloc;
+    if (aes->aes_wcs == NULL)
+      __archive_errx(1, "No memory for aes_get_wcs()");
+    mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
+    aes->aes_wcs_alloc[wcs_length] = 0;
+  }
+  return (aes->aes_wcs);
+}
+
+  static void
+aes_set_mbs(struct aes *aes, const char *mbs)
+{
+  if (aes->aes_mbs_alloc) {
+    free(aes->aes_mbs_alloc);
+    aes->aes_mbs_alloc = NULL;
+  }
+  if (aes->aes_wcs_alloc) {
+    free(aes->aes_wcs_alloc);
+    aes->aes_wcs_alloc = NULL;
+  }
+  aes->aes_mbs = mbs;
+  aes->aes_wcs = NULL;
+}
+
+  static void
+aes_copy_mbs(struct aes *aes, const char *mbs)
+{
+  if (aes->aes_mbs_alloc) {
+    free(aes->aes_mbs_alloc);
+    aes->aes_mbs_alloc = NULL;
+  }
+  if (aes->aes_wcs_alloc) {
+    free(aes->aes_wcs_alloc);
+    aes->aes_wcs_alloc = NULL;
+  }
+  aes->aes_mbs_alloc = (char*)malloc((strlen(mbs) + 1) * sizeof(char));
+  if (aes->aes_mbs_alloc == NULL)
+    __archive_errx(1, "No memory for aes_copy_mbs()");
+  strcpy(aes->aes_mbs_alloc, mbs);
+  aes->aes_mbs = aes->aes_mbs_alloc;
+  aes->aes_wcs = NULL;
+}
+
+#if 0
+  static void
+aes_set_wcs(struct aes *aes, const wchar_t *wcs)
+{
+  if (aes->aes_mbs_alloc) {
+    free(aes->aes_mbs_alloc);
+    aes->aes_mbs_alloc = NULL;
+  }
+  if (aes->aes_wcs_alloc) {
+    free(aes->aes_wcs_alloc);
+    aes->aes_wcs_alloc = NULL;
+  }
+  aes->aes_mbs = NULL;
+  aes->aes_wcs = wcs;
+}
+#endif
+
+  static void
+aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
+{
+  if (aes->aes_mbs_alloc) {
+    free(aes->aes_mbs_alloc);
+    aes->aes_mbs_alloc = NULL;
+  }
+  if (aes->aes_wcs_alloc) {
+    free(aes->aes_wcs_alloc);
+    aes->aes_wcs_alloc = NULL;
+  }
+  aes->aes_mbs = NULL;
+  aes->aes_wcs_alloc = (wchar_t*)malloc((wcslen(wcs) + 1) * sizeof(wchar_t));
+  if (aes->aes_wcs_alloc == NULL)
+    __archive_errx(1, "No memory for aes_copy_wcs()");
+  wcscpy(aes->aes_wcs_alloc, wcs);
+  aes->aes_wcs = aes->aes_wcs_alloc;
+}
+
+  struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+  aes_clean(&entry->ae_fflags_text);
+  aes_clean(&entry->ae_gname);
+  aes_clean(&entry->ae_hardlink);
+  aes_clean(&entry->ae_pathname);
+  aes_clean(&entry->ae_symlink);
+  aes_clean(&entry->ae_uname);
+  archive_entry_acl_clear(entry);
+  memset(entry, 0, sizeof(*entry));
+  return entry;
+}
+
+  struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+  struct archive_entry *entry2;
+
+  /* Allocate new structure and copy over all of the fields. */
+  entry2 = (struct archive_entry*)malloc(sizeof(*entry2));
+  if (entry2 == NULL)
+    return (NULL);
+  memset(entry2, 0, sizeof(*entry2));
+  entry2->ae_stat = entry->ae_stat;
+  entry2->ae_fflags_set = entry->ae_fflags_set;
+  entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+  aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+  aes_copy(&entry2->ae_gname, &entry->ae_gname);
+  aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+  aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
+  aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
+  aes_copy(&entry2->ae_uname, &entry->ae_uname);
+
+  /* XXX TODO: Copy ACL data over as well. XXX */
+  return (entry2);
+}
+
+  void
+archive_entry_free(struct archive_entry *entry)
+{
+  archive_entry_clear(entry);
+  free(entry);
+}
+
+  struct archive_entry *
+archive_entry_new(void)
+{
+  struct archive_entry *entry;
+
+  entry = (struct archive_entry*)malloc(sizeof(*entry));
+  if (entry == NULL)
+    return (NULL);
+  memset(entry, 0, sizeof(*entry));
+  return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+  time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_atime);
+}
+
+  long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+  (void)entry; /* entry can be unused here. */
+  return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat));
+}
+
+  time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_ctime);
+}
+
+  long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+  (void)entry; /* entry can be unused here. */
+  return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat));
+}
+
+  dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_dev);
+}
+
+  void
+archive_entry_fflags(struct archive_entry *entry,
+  unsigned long *set, unsigned long *clear)
+{
+  *set = entry->ae_fflags_set;
+  *clear = entry->ae_fflags_clear;
+}
+
+/*
+ * Note: if text was provided, this just returns that text.  If you
+ * really need the text to be rebuilt in a canonical form, set the
+ * text, ask for the bitmaps, then set the bitmaps.  (Setting the
+ * bitmaps clears any stored text.)  This design is deliberate: if
+ * we're editing archives, we don't want to discard flags just because
+ * they aren't supported on the current system.  The bitmap<->text
+ * conversions are platform-specific (see below).
+ */
+  const char *
+archive_entry_fflags_text(struct archive_entry *entry)
+{
+  const char *f;
+  char *p;
+
+  f = aes_get_mbs(&entry->ae_fflags_text);
+  if (f != NULL)
+    return (f);
+
+  if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
+    return (NULL);
+
+  p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
+  if (p == NULL)
+    return (NULL);
+
+  aes_copy_mbs(&entry->ae_fflags_text, p);
+  free(p);
+  f = aes_get_mbs(&entry->ae_fflags_text);
+  return (f);
+}
+
+  gid_t
+archive_entry_gid(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_gid);
+}
+
+  const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+  return (aes_get_mbs(&entry->ae_gname));
+}
+
+  const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+  return (aes_get_wcs(&entry->ae_gname));
+}
+
+  const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+  return (aes_get_mbs(&entry->ae_hardlink));
+}
+
+  const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+  return (aes_get_wcs(&entry->ae_hardlink));
+}
+
+  ino_t
+archive_entry_ino(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_ino);
+}
+
+  mode_t
+archive_entry_mode(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_mode);
+}
+
+  time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_mtime);
+}
+
+  long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+  (void)entry; /* entry can be unused here. */
+  return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat));
+}
+
+  const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+  return (aes_get_mbs(&entry->ae_pathname));
+}
+
+  const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+  return (aes_get_wcs(&entry->ae_pathname));
+}
+
+  dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_rdev);
+}
+
+  int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_size);
+}
+
+  const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+  return (&entry->ae_stat);
+}
+
+  const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+  return (aes_get_mbs(&entry->ae_symlink));
+}
+
+  const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+  return (aes_get_wcs(&entry->ae_symlink));
+}
+
+  uid_t
+archive_entry_uid(struct archive_entry *entry)
+{
+  return (entry->ae_stat.st_uid);
+}
+
+  const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+  return (aes_get_mbs(&entry->ae_uname));
+}
+
+  const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+  return (aes_get_wcs(&entry->ae_uname));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+/*
+ * Note "copy" not "set" here.  The "set" functions that accept a pointer
+ * only store the pointer; they don't copy the underlying object.
+ */
+  void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+  entry->ae_stat = *st;
+}
+
+  void
+archive_entry_set_fflags(struct archive_entry *entry,
+  unsigned long set, unsigned long clear)
+{
+  aes_clean(&entry->ae_fflags_text);
+  entry->ae_fflags_set = set;
+  entry->ae_fflags_clear = clear;
+}
+
+  const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+  const wchar_t *flags)
+{
+  aes_copy_wcs(&entry->ae_fflags_text, flags);
+  return (ae_wcstofflags(flags,
+      &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+  void
+archive_entry_set_gid(struct archive_entry *entry, gid_t g)
+{
+  entry->ae_stat.st_gid = g;
+}
+
+  void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+  aes_set_mbs(&entry->ae_gname, name);
+}
+
+  void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+  aes_copy_wcs(&entry->ae_gname, name);
+}
+
+  void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+  aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+  void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+  aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+  void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+  aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
+  void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+  (void)ns;
+  entry->ae_stat.st_atime = t;
+  ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns);
+}
+
+  void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+  (void)ns;
+  entry->ae_stat.st_ctime = t;
+  ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+  void
+archive_entry_set_link(struct archive_entry *entry, const char *target)
+{
+  if (entry->ae_symlink.aes_mbs != NULL ||
+    entry->ae_symlink.aes_wcs != NULL)
+    aes_set_mbs(&entry->ae_symlink, target);
+  else
+    aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+  void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+  entry->ae_stat.st_mode = m;
+}
+
+  void
+archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
+{
+  (void)ns;
+  entry->ae_stat.st_mtime = m;
+  ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns);
+}
+
+  void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+  aes_set_mbs(&entry->ae_pathname, name);
+}
+
+  void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+  aes_copy_mbs(&entry->ae_pathname, name);
+}
+
+  void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+  aes_copy_wcs(&entry->ae_pathname, name);
+}
+
+  void
+archive_entry_set_size(struct archive_entry *entry, int64_t s)
+{
+  entry->ae_stat.st_size = s;
+}
+
+  void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+  aes_set_mbs(&entry->ae_symlink, linkname);
+}
+
+  void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+  aes_copy_wcs(&entry->ae_symlink, linkname);
+}
+
+  void
+archive_entry_set_uid(struct archive_entry *entry, uid_t u)
+{
+  entry->ae_stat.st_uid = u;
+}
+
+  void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+  aes_set_mbs(&entry->ae_uname, name);
+}
+
+  void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+  aes_copy_wcs(&entry->ae_uname, name);
+}
+
+/*
+ * ACL management.  The following would, of course, be a lot simpler
+ * if: 1) the last draft of POSIX.1e were a really thorough and
+ * complete standard that addressed the needs of ACL archiving and 2)
+ * everyone followed it faithfully.  Alas, neither is true, so the
+ * following is a lot more complex than might seem necessary to the
+ * uninitiated.
+ */
+
+  void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+  struct ae_acl *ap;
+
+  while (entry->acl_head != NULL) {
+    ap = entry->acl_head->next;
+    aes_clean(&entry->acl_head->name);
+    free(entry->acl_head);
+    entry->acl_head = ap;
+  }
+  if (entry->acl_text_w != NULL) {
+    free(entry->acl_text_w);
+    entry->acl_text_w = NULL;
+  }
+  entry->acl_p = NULL;
+  entry->acl_state = 0; /* Not counting. */
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+  void
+archive_entry_acl_add_entry(struct archive_entry *entry,
+  int type, int permset, int tag, int id, const char *name)
+{
+  struct ae_acl *ap;
+
+  if (acl_special(entry, type, permset, tag) == 0)
+    return;
+  ap = acl_new_entry(entry, type, permset, tag, id);
+  if (ap == NULL) {
+    /* XXX Error XXX */
+    return;
+  }
+  if (name != NULL  &&  *name != '\0')
+    aes_copy_mbs(&ap->name, name);
+  else
+    aes_clean(&ap->name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+  void
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+  int type, int permset, int tag, int id, const wchar_t *name)
+{
+  struct ae_acl *ap;
+
+  if (acl_special(entry, type, permset, tag) == 0)
+    return;
+  ap = acl_new_entry(entry, type, permset, tag, id);
+  if (ap == NULL) {
+    /* XXX Error XXX */
+    return;
+  }
+  if (name != NULL  &&  *name != L'\0')
+    aes_copy_wcs(&ap->name, name);
+  else
+    aes_clean(&ap->name);
+}
+
+/*
+ * If this ACL entry is part of the standard POSIX permissions set,
+ * store the permissions in the stat structure and return zero.
+ */
+  static int
+acl_special(struct archive_entry *entry, int type, int permset, int tag)
+{
+  if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+    switch (tag) {
+    case ARCHIVE_ENTRY_ACL_USER_OBJ:
+      entry->ae_stat.st_mode &= ~0700;
+      entry->ae_stat.st_mode |= (permset & 7) << 6;
+      return (0);
+    case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+      entry->ae_stat.st_mode &= ~0070;
+      entry->ae_stat.st_mode |= (permset & 7) << 3;
+      return (0);
+    case ARCHIVE_ENTRY_ACL_OTHER:
+      entry->ae_stat.st_mode &= ~0007;
+      entry->ae_stat.st_mode |= permset & 7;
+      return (0);
+    }
+  }
+  return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+  static struct ae_acl *
+acl_new_entry(struct archive_entry *entry,
+  int type, int permset, int tag, int id)
+{
+  struct ae_acl *ap;
+
+  if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
+    type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+    return (NULL);
+  if (entry->acl_text_w != NULL) {
+    free(entry->acl_text_w);
+    entry->acl_text_w = NULL;
+  }
+
+  /* XXX TODO: More sanity-checks on the arguments XXX */
+
+  /* If there's a matching entry already in the list, overwrite it. */
+  for (ap = entry->acl_head; ap != NULL; ap = ap->next) {
+    if (ap->type == type && ap->tag == tag && ap->id == id) {
+      ap->permset = permset;
+      return (ap);
+    }
+  }
+
+  /* Add a new entry to the list. */
+  ap = (struct ae_acl *)malloc(sizeof(*ap));
+  if (ap == NULL)
+    return (NULL);
+  memset(ap, 0, sizeof(*ap));
+  ap->next = entry->acl_head;
+  entry->acl_head = ap;
+  ap->type = type;
+  ap->tag = tag;
+  ap->id = id;
+  ap->permset = permset;
+  return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+  int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+  int count;
+  struct ae_acl *ap;
+
+  count = 0;
+  ap = entry->acl_head;
+  while (ap != NULL) {
+    if ((ap->type & want_type) != 0)
+      count++;
+    ap = ap->next;
+  }
+
+  if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
+    count += 3;
+  return (count);
+}
+
+/*
+ * Prepare for reading entries from the ACL data.  Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+  int
+archive_entry_acl_reset(struct archive_entry *entry, int want_type)
+{
+  int count, cutoff;
+
+  count = archive_entry_acl_count(entry, want_type);
+
+  /*
+   * If the only entries are the three standard ones,
+   * then don't return any ACL data.  (In this case,
+   * client can just use chmod(2) to set permissions.)
+   */
+  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+    cutoff = 3;
+  else
+    cutoff = 0;
+
+  if (count > cutoff)
+    entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+  else
+    entry->acl_state = 0;
+  entry->acl_p = entry->acl_head;
+  return (count);
+}
+
+/*
+ * Return the next ACL entry in the list.  Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+
+  int
+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
+  int *permset, int *tag, int *id, const char **name)
+{
+  *name = NULL;
+  *id = -1;
+
+  /*
+   * The acl_state is either zero (no entries available), -1
+   * (reading from list), or an entry type (retrieve that type
+   * from ae_stat.st_mode).
+   */
+  if (entry->acl_state == 0)
+    return (ARCHIVE_WARN);
+
+  /* The first three access entries are special. */
+  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+    switch (entry->acl_state) {
+    case ARCHIVE_ENTRY_ACL_USER_OBJ:
+      *permset = (entry->ae_stat.st_mode >> 6) & 7;
+      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+      *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+      entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+      return (ARCHIVE_OK);
+    case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+      *permset = (entry->ae_stat.st_mode >> 3) & 7;
+      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+      *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+      entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+      return (ARCHIVE_OK);
+    case ARCHIVE_ENTRY_ACL_OTHER:
+      *permset = entry->ae_stat.st_mode & 7;
+      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+      *tag = ARCHIVE_ENTRY_ACL_OTHER;
+      entry->acl_state = -1;
+      entry->acl_p = entry->acl_head;
+      return (ARCHIVE_OK);
+    default:
+      break;
+    }
+  }
+
+  while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
+    entry->acl_p = entry->acl_p->next;
+  if (entry->acl_p == NULL) {
+    entry->acl_state = 0;
+    return (ARCHIVE_WARN);
+  }
+  *type = entry->acl_p->type;
+  *permset = entry->acl_p->permset;
+  *tag = entry->acl_p->tag;
+  *id = entry->acl_p->id;
+  *name = aes_get_mbs(&entry->acl_p->name);
+  entry->acl_p = entry->acl_p->next;
+  return (ARCHIVE_OK);
+}
+
+/*
+ * Generate a text version of the ACL.  The flags parameter controls
+ * the style of the generated ACL.
+ */
+  const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+  int count;
+  int length;
+  const wchar_t *wname;
+  const wchar_t *prefix;
+  wchar_t separator;
+  struct ae_acl *ap;
+  int id;
+  wchar_t *wp;
+
+  if (entry->acl_text_w != NULL) {
+    free (entry->acl_text_w);
+    entry->acl_text_w = NULL;
+  }
+
+  separator = L',';
+  count = 0;
+  length = 0;
+  ap = entry->acl_head;
+  while (ap != NULL) {
+    if ((ap->type & flags) != 0) {
+      count++;
+      if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
+        (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
+        length += 8; /* "default:" */
+      length += 5; /* tag name */
+      length += 1; /* colon */
+      wname = aes_get_wcs(&ap->name);
+      if (wname != NULL)
+        length += wcslen(wname);
+      length ++; /* colon */
+      length += 3; /* rwx */
+      length += 1; /* colon */
+      length += max(sizeof(uid_t),sizeof(gid_t)) * 3 + 1;
+      length ++; /* newline */
+    }
+    ap = ap->next;
+  }
+
+  if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
+    length += 10; /* "user::rwx\n" */
+    length += 11; /* "group::rwx\n" */
+    length += 11; /* "other::rwx\n" */
+  }
+
+  if (count == 0)
+    return (NULL);
+
+  /* Now, allocate the string and actually populate it. */
+  wp = entry->acl_text_w = (wchar_t*)malloc(length * sizeof(wchar_t));
+  if (wp == NULL)
+    __archive_errx(1, "No memory to generate the text version of the ACL");
+  count = 0;
+  if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+    append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+      entry->ae_stat.st_mode & 0700, -1);
+    *wp++ = ',';
+    append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+      entry->ae_stat.st_mode & 0070, -1);
+    *wp++ = ',';
+    append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+      entry->ae_stat.st_mode & 0007, -1);
+    count += 3;
+
+    ap = entry->acl_head;
+    while (ap != NULL) {
+      if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+        wname = aes_get_wcs(&ap->name);
+        *wp++ = separator;
+        if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+          id = ap->id;
+        else
+          id = -1;
+        append_entry_w(&wp, NULL, ap->tag, wname,
+          ap->permset, id);
+        count++;
+      }
+      ap = ap->next;
+    }
+  }
+
+
+  if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+    if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+      prefix = L"default:";
+    else
+      prefix = NULL;
+    ap = entry->acl_head;
+    count = 0;
+    while (ap != NULL) {
+      if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+        wname = aes_get_wcs(&ap->name);
+        if (count > 0)
+          *wp++ = separator;
+        if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+          id = ap->id;
+        else
+          id = -1;
+        append_entry_w(&wp, prefix, ap->tag,
+          wname, ap->permset, id);
+        count ++;
+      }
+      ap = ap->next;
+    }
+  }
+
+  return (entry->acl_text_w);
+}
+
+  static void
+append_id_w(wchar_t **wp, int id)
+{
+  if (id > 9)
+    append_id_w(wp, id / 10);
+  *(*wp)++ = L"0123456789"[id % 10];
+}
+
+  static void
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+  const wchar_t *wname, int perm, int id)
+{
+  if (prefix != NULL) {
+    wcscpy(*wp, prefix);
+    *wp += wcslen(*wp);
+  }
+  switch (tag) {
+  case ARCHIVE_ENTRY_ACL_USER_OBJ:
+    wname = NULL;
+    id = -1;
+    /* FALL THROUGH */
+  case ARCHIVE_ENTRY_ACL_USER:
+    wcscpy(*wp, L"user");
+    break;
+  case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+    wname = NULL;
+    id = -1;
+    /* FALL THROUGH */
+  case ARCHIVE_ENTRY_ACL_GROUP:
+    wcscpy(*wp, L"group");
+    break;
+  case ARCHIVE_ENTRY_ACL_MASK:
+    wcscpy(*wp, L"mask");
+    wname = NULL;
+    id = -1;
+    break;
+  case ARCHIVE_ENTRY_ACL_OTHER:
+    wcscpy(*wp, L"other");
+    wname = NULL;
+    id = -1;
+    break;
+  }
+  *wp += wcslen(*wp);
+  *(*wp)++ = L':';
+  if (wname != NULL) {
+    wcscpy(*wp, wname);
+    *wp += wcslen(*wp);
+  }
+  *(*wp)++ = L':';
+  *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+  *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+  *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+  if (id != -1) {
+    *(*wp)++ = L':';
+    append_id_w(wp, id);
+  }
+  **wp = L'\0';
+}
+
+/*
+ * Parse a textual ACL.  This automatically recognizes and supports
+ * extensions described above.  The 'type' argument is used to
+ * indicate the type that should be used for any entries not
+ * explicitly marked as "default:".
+ */
+  int
+__archive_entry_acl_parse_w(struct archive_entry *entry,
+  const wchar_t *text, int default_type)
+{
+  int type, tag, permset, id;
+  const wchar_t *start, *end;
+  const wchar_t *name_start, *name_end;
+  wchar_t sep;
+  wchar_t *namebuff;
+  int namebuff_length;
+
+  name_start = name_end = NULL;
+  namebuff = NULL;
+  namebuff_length = 0;
+
+  while (text != NULL  &&  *text != L'\0') {
+    next_field_w(&text, &start, &end, &sep);
+    if (sep != L':')
+      goto fail;
+
+    /*
+     * Solaris extension:  "defaultuser::rwx" is the
+     * default ACL corresponding to "user::rwx", etc.
+     */
+    if (end-start > 7  && wmemcmp(start, L"default", 7) == 0) {
+      type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+      start += 7;
+    } else
+      type = default_type;
+
+    if (prefix_w(start, end, L"user")) {
+      next_field_w(&text, &start, &end, &sep);
+      if (sep != L':')
+        goto fail;
+      if (end > start) {
+        tag = ARCHIVE_ENTRY_ACL_USER;
+        name_start = start;
+        name_end = end;
+      } else
+        tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+    } else if (prefix_w(start, end, L"group")) {
+      next_field_w(&text, &start, &end, &sep);
+      if (sep != L':')
+        goto fail;
+      if (end > start) {
+        tag = ARCHIVE_ENTRY_ACL_GROUP;
+        name_start = start;
+        name_end = end;
+      } else
+        tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+    } else if (prefix_w(start, end, L"other")) {
+      next_field_w(&text, &start, &end, &sep);
+      if (sep != L':')
+        goto fail;
+      if (end > start)
+        goto fail;
+      tag = ARCHIVE_ENTRY_ACL_OTHER;
+    } else if (prefix_w(start, end, L"mask")) {
+      next_field_w(&text, &start, &end, &sep);
+      if (sep != L':')
+        goto fail;
+      if (end > start)
+        goto fail;
+      tag = ARCHIVE_ENTRY_ACL_MASK;
+    } else
+      goto fail;
+
+    next_field_w(&text, &start, &end, &sep);
+    permset = 0;
+    while (start < end) {
+      switch (*start++) {
+      case 'r': case 'R':
+        permset |= ARCHIVE_ENTRY_ACL_READ;
+        break;
+      case 'w': case 'W':
+        permset |= ARCHIVE_ENTRY_ACL_WRITE;
+        break;
+      case 'x': case 'X':
+        permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+        break;
+      case '-':
+        break;
+      default:
+        goto fail;
+      }
+    }
+
+    /*
+     * Support star-compatible numeric UID/GID extension.
+     * This extension adds a ":" followed by the numeric
+     * ID so that "group:groupname:rwx", for example,
+     * becomes "group:groupname:rwx:999", where 999 is the
+     * numeric GID.  This extension makes it possible, for
+     * example, to correctly restore ACLs on a system that
+     * might have a damaged passwd file or be disconnected
+     * from a central NIS server.  This extension is compatible
+     * with POSIX.1e draft 17.
+     */
+    if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER ||
+        tag == ARCHIVE_ENTRY_ACL_GROUP)) {
+      next_field_w(&text, &start, &end, &sep);
+
+      id = 0;
+      while (start < end  && *start >= '0' && *start <= '9') {
+        if (id > (INT_MAX / 10))
+          id = INT_MAX;
+        else {
+          id *= 10;
+          id += *start - '0';
+          start++;
+        }
+      }
+    } else
+          id = -1; /* No id specified. */
+
+    /* Skip any additional entries. */
+    while (sep == L':') {
+      next_field_w(&text, &start, &end, &sep);
+    }
+
+    /* Add entry to the internal list. */
+    if (name_end == name_start) {
+      archive_entry_acl_add_entry_w(entry, type, permset,
+        tag, id, NULL);
+    } else {
+      if (namebuff_length <= name_end - name_start) {
+        if (namebuff != NULL)
+          free(namebuff);
+        namebuff_length = name_end - name_start + 256;
+        namebuff =
+          (wchar_t*)malloc(namebuff_length * sizeof(wchar_t));
+        if (namebuff == NULL)
+          goto fail;
+      }
+      wmemcpy(namebuff, name_start, name_end - name_start);
+      namebuff[name_end - name_start] = L'\0';
+      archive_entry_acl_add_entry_w(entry, type,
+        permset, tag, id, namebuff);
+    }
+  }
+  if (namebuff != NULL)
+    free(namebuff);
+  return (ARCHIVE_OK);
+
+fail:
+  if (namebuff != NULL)
+    free(namebuff);
+  return (ARCHIVE_WARN);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
+ * to point to just after the separator.  *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier.  In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+  static void
+next_field_w(const wchar_t **wp, const wchar_t **start,
+  const wchar_t **end, wchar_t *sep)
+{
+  /* Skip leading whitespace to find start of field. */
+  while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
+    (*wp)++;
+  }
+  *start = *wp;
+
+  /* Scan for the separator. */
+  while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
+    **wp != L'\n') {
+    (*wp)++;
+  }
+  *sep = **wp;
+
+  /* Trim trailing whitespace to locate end of field. */
+  *end = *wp - 1;
+  while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+    (*end)--;
+  }
+  (*end)++;
+
+  /* Adjust scanner location. */
+  if (**wp != L'\0')
+    (*wp)++;
+}
+
+  static int
+prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
+{
+  if (start == end)
+    return (0);
+
+  if (*start++ != *test++)
+    return (0);
+
+  while (start < end  &&  *start++ == *test++)
+    ;
+
+  if (start < end)
+    return (0);
+
+  return (1);
+}
+
+
+/*
+ * Following code is modified from UC Berkeley sources, and
+ * is subject to the following copyright notice.
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static struct flag {
+  const char *name;
+  const wchar_t *wname;
+  unsigned long  set;
+  unsigned long  clear;
+} flags[] = {
+  /* Preferred (shorter) names per flag first, all prefixed by "no" */
+#ifdef SF_APPEND
+    { "nosappnd", L"nosappnd",  SF_APPEND, 0 },
+    { "nosappend", L"nosappend",  SF_APPEND, 0 },
+#endif
+#ifdef  EXT2_APPEND_FL    /* 'a' */
+    { "nosappnd", L"nosappnd",  EXT2_APPEND_FL, 0 },
+    { "nosappend", L"nosappend",  EXT2_APPEND_FL, 0 },
+#endif
+#ifdef SF_ARCHIVED
+    { "noarch", L"noarch",  SF_ARCHIVED, 0 },
+    { "noarchived", L"noarchived",        SF_ARCHIVED, 0 },
+#endif
+#ifdef SF_IMMUTABLE
+    { "noschg", L"noschg",  SF_IMMUTABLE, 0 },
+    { "noschange", L"noschange",  SF_IMMUTABLE, 0 },
+    { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 },
+#endif
+#ifdef EXT2_IMMUTABLE_FL   /* 'i' */
+    { "noschg", L"noschg",  EXT2_IMMUTABLE_FL, 0 },
+    { "noschange", L"noschange",  EXT2_IMMUTABLE_FL, 0 },
+    { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 },
+#endif
+#ifdef SF_NOUNLINK
+    { "nosunlnk", L"nosunlnk",  SF_NOUNLINK, 0 },
+    { "nosunlink", L"nosunlink",  SF_NOUNLINK, 0 },
+#endif
+#ifdef SF_SNAPSHOT
+    { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 },
+#endif
+#ifdef UF_APPEND
+    { "nouappnd", L"nouappnd",  UF_APPEND, 0 },
+    { "nouappend", L"nouappend",  UF_APPEND, 0 },
+#endif
+#ifdef UF_IMMUTABLE
+    { "nouchg", L"nouchg",  UF_IMMUTABLE, 0 },
+    { "nouchange", L"nouchange",  UF_IMMUTABLE, 0 },
+    { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 },
+#endif
+#ifdef UF_NODUMP
+    { "nodump", L"nodump",  0,  UF_NODUMP},
+#endif
+#ifdef EXT2_NODUMP_FL    /* 'd' */
+    { "nodump", L"nodump",  0,  EXT2_NODUMP_FL},
+#endif
+#ifdef UF_OPAQUE
+    { "noopaque", L"noopaque",  UF_OPAQUE, 0 },
+#endif
+#ifdef UF_NOUNLINK
+    { "nouunlnk", L"nouunlnk",  UF_NOUNLINK, 0 },
+    { "nouunlink", L"nouunlink",  UF_NOUNLINK, 0 },
+#endif
+#ifdef EXT2_COMPR_FL    /* 'c' */
+    { "nocompress", L"nocompress",        EXT2_COMPR_FL, 0 },
+#endif
+
+#ifdef EXT2_NOATIME_FL    /* 'A' */
+    { "noatime", L"noatime",  0,  EXT2_NOATIME_FL},
+#endif
+    { NULL,  NULL,   0,  0 }
+};
+
+/*
+ * fflagstostr --
+ * Convert file flags to a comma-separated string.  If no flags
+ * are set, return the empty string.
+ */
+  char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+  char *string, *dp;
+  const char *sp;
+  unsigned long bits;
+  struct flag *flag;
+  int length;
+
+  bits = bitset | bitclear;
+  length = 0;
+  for (flag = flags; flag->name != NULL; flag++)
+    if (bits & (flag->set | flag->clear)) {
+      length += strlen(flag->name) + 1;
+      bits &= ~(flag->set | flag->clear);
+    }
+
+  if (length == 0)
+    return (NULL);
+  string = (char*)malloc(length);
+  if (string == NULL)
+    return (NULL);
+
+  dp = string;
+  for (flag = flags; flag->name != NULL; flag++) {
+    if (bitset & flag->set || bitclear & flag->clear) {
+      sp = flag->name + 2;
+    } else if (bitset & flag->clear  ||  bitclear & flag->set) {
+      sp = flag->name;
+    } else
+      continue;
+    bitset &= ~(flag->set | flag->clear);
+    bitclear &= ~(flag->set | flag->clear);
+    if (dp > string)
+      *dp++ = ',';
+    while ((*dp++ = *sp++) != '\0')
+      ;
+    dp--;
+  }
+
+  *dp = '\0';
+  return (string);
+}
+
+/*
+ * wcstofflags --
+ * Take string of arguments and return file flags.  This
+ * version works a little differently than strtofflags(3).
+ * In particular, it always tests every token, skipping any
+ * unrecognized tokens.  It returns a pointer to the first
+ * unrecognized token, or NULL if every token was recognized.
+ * This version is also const-correct and does not modify the
+ * provided string.
+ */
+  const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+  const wchar_t *start, *end;
+  struct flag *flag;
+  unsigned long set, clear;
+  const wchar_t *failed;
+
+  set = clear = 0;
+  start = s;
+  failed = NULL;
+  /* Find start of first token. */
+  while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+    start++;
+  while (*start != L'\0') {
+    /* Locate end of token. */
+    end = start;
+    while (*end != L'\0'  &&  *end != L'\t'  &&
+      *end != L' '  &&  *end != L',')
+      end++;
+    for (flag = flags; flag->wname != NULL; flag++) {
+      if (wmemcmp(start, flag->wname, end - start) == 0) {
+        /* Matched "noXXXX", so reverse the sense. */
+        clear |= flag->set;
+        set |= flag->clear;
+        break;
+      } else if (wmemcmp(start, flag->wname + 2, end - start)
+        == 0) {
+        /* Matched "XXXX", so don't reverse. */
+        set |= flag->set;
+        clear |= flag->clear;
+        break;
+      }
+    }
+    /* Ignore unknown flag names. */
+    if (flag->wname == NULL  &&  failed == NULL)
+      failed = start;
+
+    /* Find start of next token. */
+    start = end;
+    while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+      start++;
+
+  }
+
+  if (setp)
+    *setp = set;
+  if (clrp)
+    *clrp = clear;
+
+  /* Return location of first failure. */
+  return (failed);
+}
+
+//--- archive_string_sprintf.c
+/*
+ * Like 'vsprintf', but ensures the target is big enough, resizing if
+ * necessary.
+ */
+  void
+__archive_string_vsprintf(struct archive_string *as, const char *fmt,
+  va_list ap)
+{
+  char long_flag;
+  intmax_t s; /* Signed integer temp. */
+  uintmax_t u; /* Unsigned integer temp. */
+  const char *p, *p2;
+
+  __archive_string_ensure(as, 64);
+
+  if (fmt == NULL) {
+    as->s[0] = 0;
+    return;
+  }
+
+  long_flag = '\0';
+  for (p = fmt; *p != '\0'; p++) {
+    const char *saved_p = p;
+
+    if (*p != '%') {
+      archive_strappend_char(as, *p);
+      continue;
+    }
+
+    p++;
+
+    switch(*p) {
+    case 'j':
+      long_flag = 'j';
+      p++;
+      break;
+    case 'l':
+      long_flag = 'l';
+      p++;
+      break;
+    }
+
+    switch (*p) {
+    case '%':
+      __archive_strappend_char(as, '%');
+      break;
+    case 'c':
+      s = va_arg(ap, int);
+      __archive_strappend_char(as, s);
+      break;
+    case 'd':
+      switch(long_flag) {
+      case 'j': s = va_arg(ap, intmax_t); break;
+      case 'l': s = va_arg(ap, long); break;
+      default:  s = va_arg(ap, int); break;
+      }
+      archive_strappend_int(as, s, 10);
+      break;
+    case 's':
+      p2 = va_arg(ap, char *);
+      archive_strcat(as, p2);
+      break;
+    case 'o': case 'u': case 'x': case 'X':
+      /* Common handling for unsigned integer formats. */
+      switch(long_flag) {
+      case 'j': u = va_arg(ap, uintmax_t); break;
+      case 'l': u = va_arg(ap, unsigned long); break;
+      default:  u = va_arg(ap, unsigned int); break;
+      }
+      /* Format it in the correct base. */
+      switch (*p) {
+      case 'o': archive_strappend_int(as, u, 8); break;
+      case 'u': archive_strappend_int(as, u, 10); break;
+      default: archive_strappend_int(as, u, 16); break;
+      }
+      break;
+    default:
+      /* Rewind and print the initial '%' literally. */
+      p = saved_p;
+      archive_strappend_char(as, *p);
+    }
+  }
+}
+
+//----------------------------------------------------------------------
+class cmCPackTGZ_Data
+{
+public:
+  cmCPackTGZ_Data(cmCPackTGZGenerator* gen) :
+    Name(0), OutputStream(0), Generator(gen) {}
+  const char *Name;
+  std::ostream* OutputStream;
+  cmCPackTGZGenerator* Generator;
+};
+
+//----------------------------------------------------------------------
+int cmCPackTGZGenerator::TGZ_Open(struct archive *a, void *client_data)
+{
+  cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data;
+
+  (void)a;
+  mydata->OutputStream = new cmGeneratedFileStream(mydata->Name);
+  if ( *mydata->OutputStream &&
+    mydata->Generator->GenerateHeader(mydata->OutputStream))
+    {
+    return (ARCHIVE_OK);
+    }
+  else
+    {
+    return (ARCHIVE_FATAL);
+    }
+}
+
+//----------------------------------------------------------------------
+ssize_t cmCPackTGZGenerator::TGZ_Write(struct archive *a, void *client_data, void *buff, size_t n)
+{
+  cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data;
+
+  (void)a;
+  mydata->OutputStream->write(reinterpret_cast<const char*>(buff), n);
+  if ( !*mydata->OutputStream )
+    {
+    return 0;
+    }
+  return n;
+}
+
+//----------------------------------------------------------------------
+int cmCPackTGZGenerator::TGZ_Close(struct archive *a, void *client_data)
+{
+  cmCPackTGZ_Data *mydata = (cmCPackTGZ_Data*)client_data;
+
+  (void)a;
+  delete mydata->OutputStream;
+  return (0);
+}
+
+//----------------------------------------------------------------------
+int cmCPackTGZGenerator::CompressFiles(const char* outFileName, const char* toplevel,
+  const std::vector<std::string>& files)
+{
+  std::cout << "Toplevel: " << toplevel << std::endl;
+  cmCPackTGZ_Data mydata(this);
+  struct archive *a;
+  struct archive_entry *entry;
+  struct stat st;
+  char buff[8192];
+  int len;
+  int fd;
+
+  a = archive_write_new();
+  mydata.Name = outFileName;
+  archive_write_set_compression_gzip(a);
+  archive_write_set_format_ustar(a);
+  archive_write_open(a, &mydata, cmCPackTGZGenerator::TGZ_Open,
+    cmCPackTGZGenerator::TGZ_Write, cmCPackTGZGenerator::TGZ_Close);
+  std::vector<std::string>::const_iterator fileIt;
+  for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
+    {
+    std::string fname = cmSystemTools::RelativePath(toplevel, fileIt->c_str());
+    const char* filename = fileIt->c_str();
+    stat(filename, &st);
+    entry = archive_entry_new();
+    archive_entry_copy_stat(entry, &st);
+    archive_entry_set_pathname(entry, fname.c_str());
+    archive_write_header(a, entry);
+    fd = open(filename, O_RDONLY);
+    len = read(fd, buff, sizeof(buff));
+    while ( len > 0 )
+      {
+      archive_write_data(a, buff, len);
+      len = read(fd, buff, sizeof(buff));
+      }
+    archive_entry_free(entry);
+    }
+  archive_write_finish(a);
+  return 1;
+}
+
+//--- archive_write_set_format_ustar.c
+struct ustar {
+  uint64_t entry_bytes_remaining;
+  uint64_t entry_padding;
+  char  written;
+};
+
+/*
+ * Define structure of POSIX 'ustar' tar header.
+ */
+struct archive_entry_header_ustar {
+  char name[100];
+  char mode[6];
+  char mode_padding[2];
+  char uid[6];
+  char uid_padding[2];
+  char gid[6];
+  char gid_padding[2];
+  char size[11];
+  char size_padding[1];
+  char mtime[11];
+  char mtime_padding[1];
+  char checksum[8];
+  char typeflag[1];
+  char linkname[100];
+  char magic[6]; /* For POSIX: "ustar\0" */
+  char version[2]; /* For POSIX: "00" */
+  char uname[32];
+  char gname[32];
+  char rdevmajor[6];
+  char rdevmajor_padding[2];
+  char rdevminor[6];
+  char rdevminor_padding[2];
+  char prefix[155];
+  char padding[12];
+};
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const struct archive_entry_header_ustar template_header = {
+    { "" },    /* name */
+    { '0','0','0','0','0','0' }, { ' ', '\0' }, /* mode, space-null termination. */
+    { '0','0','0','0','0','0' }, { ' ', '\0' }, /* uid, space-null termination. */
+    { '0','0','0','0','0','0' }, { ' ', '\0' }, /* gid, space-null termination. */
+    { '0','0','0','0','0','0','0','0','0','0','0' }, { ' ' }, /* size, space termination. */
+    { '0','0','0','0','0','0','0','0','0','0','0' }, { ' ' }, /* mtime, space termination. */
+    { ' ',' ',' ',' ',' ',' ',' ',' ' },   /* Initial checksum value. */
+    { '0' },   /* default: regular file */
+    { "" },    /* linkname */
+    { 'u','s','t','a','r' },   /* magic */
+    { '0', '0' },   /* version */
+    { "" },    /* uname */
+    { "" },    /* gname */
+    {'0','0','0','0','0','0'}, { ' ', '\0' }, /* rdevmajor, space-null termination */
+    {'0','0','0','0','0','0'}, { ' ', '\0' }, /* rdevminor, space-null termination */
+    { "" },    /* prefix */
+    { "" }    /* padding */
+};
+
+static int archive_write_ustar_data(struct archive *a, const void *buff,
+  size_t s);
+static int archive_write_ustar_finish(struct archive *);
+static int archive_write_ustar_finish_entry(struct archive *);
+static int archive_write_ustar_header(struct archive *,
+  struct archive_entry *entry);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int max, int strict);
+static int format_octal(int64_t, char *, int);
+static int write_nulls(struct archive *a, size_t);
+
+/*
+ * Set output format to 'ustar' format.
+ */
+  int
+archive_write_set_format_ustar(struct archive *a)
+{
+  struct ustar *ustar;
+
+  /* If someone else was already registered, unregister them. */
+  if (a->format_finish != NULL)
+    (a->format_finish)(a);
+
+  ustar = (struct ustar*)malloc(sizeof(*ustar));
+  if (ustar == NULL) {
+    archive_set_error(a, ENOMEM, "Can't allocate ustar data");
+    return (ARCHIVE_FATAL);
+  }
+  memset(ustar, 0, sizeof(*ustar));
+  a->format_data = ustar;
+
+  a->pad_uncompressed = 1; /* Mimic gtar in this respect. */
+  a->format_write_header = archive_write_ustar_header;
+  a->format_write_data = archive_write_ustar_data;
+  a->format_finish = archive_write_ustar_finish;
+  a->format_finish_entry = archive_write_ustar_finish_entry;
+  a->archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+  a->archive_format_name = "POSIX ustar";
+  return (ARCHIVE_OK);
+}
+
+  static int
+archive_write_ustar_header(struct archive *a, struct archive_entry *entry)
+{
+  char buff[512];
+  int ret;
+  struct ustar *ustar;
+
+  ustar = (struct ustar*)a->format_data;
+  ustar->written = 1;
+
+  /* Only regular files (not hardlinks) have data. */
+  if (archive_entry_hardlink(entry) != NULL ||
+    archive_entry_symlink(entry) != NULL ||
+    !S_ISREG(archive_entry_mode(entry)))
+    archive_entry_set_size(entry, 0);
+
+  ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
+  if (ret != ARCHIVE_OK)
+    return (ret);
+  ret = (a->compression_write)(a, buff, 512);
+  if (ret != ARCHIVE_OK)
+    return (ret);
+
+  ustar->entry_bytes_remaining = archive_entry_size(entry);
+  ustar->entry_padding = 0x1ff & (- ustar->entry_bytes_remaining);
+  return (ARCHIVE_OK);
+}
+
+/*
+ * Format a basic 512-byte "ustar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ * This is exported so that other 'tar' formats can use it.
+ */
+  int
+__archive_write_format_header_ustar(struct archive *a, char buff[512],
+  struct archive_entry *entry, int tartype, int strict)
+{
+  unsigned int checksum;
+  struct archive_entry_header_ustar *h;
+  int i, ret;
+  size_t copy_length;
+  const char *p, *pp;
+  const struct stat *st;
+  int mytartype;
+
+  ret = 0;
+  mytartype = -1;
+  /*
+   * The "template header" already includes the "ustar"
+   * signature, various end-of-field markers and other required
+   * elements.
+   */
+  memcpy(buff, &template_header, 512);
+
+  h = (struct archive_entry_header_ustar *)buff;
+
+  /*
+   * Because the block is already null-filled, and strings
+   * are allowed to exactly fill their destination (without null),
+   * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+   */
+
+  pp = archive_entry_pathname(entry);
+  if (strlen(pp) <= sizeof(h->name))
+    memcpy(h->name, pp, strlen(pp));
+  else {
+    /* Store in two pieces, splitting at a '/'. */
+    p = strchr(pp + strlen(pp) - sizeof(h->name) - 1, '/');
+    /*
+     * If there is no path separator, or the prefix or
+     * remaining name are too large, return an error.
+     */
+    if (!p) {
+      archive_set_error(a, ENAMETOOLONG,
+        "Pathname too long");
+      ret = ARCHIVE_WARN;
+    } else if (p  > pp + sizeof(h->prefix)) {
+      archive_set_error(a, ENAMETOOLONG,
+        "Pathname too long");
+      ret = ARCHIVE_WARN;
+    } else {
+      /* Copy prefix and remainder to appropriate places */
+      memcpy(h->prefix, pp, p - pp);
+      memcpy(h->name, p + 1, pp + strlen(pp) - p - 1);
+    }
+  }
+
+  p = archive_entry_hardlink(entry);
+  if (p != NULL)
+    mytartype = '1';
+  else
+    p = archive_entry_symlink(entry);
+  if (p != NULL && p[0] != '\0') {
+    copy_length = strlen(p);
+    if (copy_length > sizeof(h->linkname)) {
+      archive_set_error(a, ENAMETOOLONG,
+        "Link contents too long");
+      ret = ARCHIVE_WARN;
+      copy_length = sizeof(h->linkname);
+    }
+    memcpy(h->linkname, p, copy_length);
+  }
+
+  p = archive_entry_uname(entry);
+  if (p != NULL && p[0] != '\0') {
+    copy_length = strlen(p);
+    if (copy_length > sizeof(h->uname)) {
+      archive_set_error(a, ARCHIVE_ERRNO_MISC,
+        "Username too long");
+      ret = ARCHIVE_WARN;
+      copy_length = sizeof(h->uname);
+    }
+    memcpy(h->uname, p, copy_length);
+  }
+
+  p = archive_entry_gname(entry);
+  if (p != NULL && p[0] != '\0') {
+    copy_length = strlen(p);
+    if (strlen(p) > sizeof(h->gname)) {
+      archive_set_error(a, ARCHIVE_ERRNO_MISC,
+        "Group name too long");
+      ret = ARCHIVE_WARN;
+      copy_length = sizeof(h->gname);
+    }
+    memcpy(h->gname, p, copy_length);
+  }
+
+  st = archive_entry_stat(entry);
+
+  if (format_number(st->st_mode & 07777, h->mode, sizeof(h->mode), 8, strict)) {
+    archive_set_error(a, ERANGE, "Numeric mode too large");
+    ret = ARCHIVE_WARN;
+  }
+
+  if (format_number(st->st_uid, h->uid, sizeof(h->uid), 8, strict)) {
+    archive_set_error(a, ERANGE, "Numeric user ID too large");
+    ret = ARCHIVE_WARN;
+  }
+
+  if (format_number(st->st_gid, h->gid, sizeof(h->gid), 8, strict)) {
+    archive_set_error(a, ERANGE, "Numeric group ID too large");
+    ret = ARCHIVE_WARN;
+  }
+
+  if (format_number(st->st_size, h->size, sizeof(h->size), 12, strict)) {
+    archive_set_error(a, ERANGE, "File size out of range");
+    ret = ARCHIVE_WARN;
+  }
+
+  if (format_number(st->st_mtime, h->mtime, sizeof(h->mtime), 12, strict)) {
+    archive_set_error(a, ERANGE,
+      "File modification time too large");
+    ret = ARCHIVE_WARN;
+  }
+
+#if defined(S_ISBLK)
+  if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
+    if (format_number(major(st->st_rdev), h->rdevmajor,
+        sizeof(h->rdevmajor), 8, strict)) {
+      archive_set_error(a, ERANGE,
+        "Major device number too large");
+      ret = ARCHIVE_WARN;
+    }
+
+    if (format_number(minor(st->st_rdev), h->rdevminor,
+        sizeof(h->rdevminor), 8, strict)) {
+      archive_set_error(a, ERANGE,
+        "Minor device number too large");
+      ret = ARCHIVE_WARN;
+    }
+  }
+#endif
+
+  if (tartype >= 0) {
+    h->typeflag[0] = tartype;
+  } else if (mytartype >= 0) {
+    h->typeflag[0] = mytartype;
+  } else {
+    switch (st->st_mode & S_IFMT) {
+    case S_IFREG: h->typeflag[0] = '0' ; break;
+#if defined(S_IFLNK)
+    case S_IFLNK: h->typeflag[0] = '2' ; break;
+#endif
+    case S_IFCHR: h->typeflag[0] = '3' ; break;
+#if defined(S_IFBLK)
+    case S_IFBLK: h->typeflag[0] = '4' ; break;
+#endif
+    case S_IFDIR: h->typeflag[0] = '5' ; break;
+#if defined(S_IFIFO)
+    case S_IFIFO: h->typeflag[0] = '6' ; break;
+#endif
+#if defined(S_IFSOCK)
+    case S_IFSOCK:
+                  archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "tar format cannot archive socket");
+                  ret = ARCHIVE_WARN;
+                  break;
+#endif
+    default:
+                  archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "tar format cannot archive this (mode=0%lo)",
+                    (unsigned long)st->st_mode);
+                  ret = ARCHIVE_WARN;
+    }
+  }
+
+  checksum = 0;
+  for (i = 0; i < 512; i++)
+    checksum += 255 & (unsigned int)buff[i];
+  h->checksum[6] = '\0'; /* Can't be pre-set in the template. */
+  /* h->checksum[7] = ' '; */ /* This is pre-set in the template. */
+  format_octal(checksum, h->checksum, 6);
+  return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+  static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+  int64_t limit;
+
+  limit = ((int64_t)1 << (s*3));
+
+  /* "Strict" only permits octal values with proper termination. */
+  if (strict)
+    return (format_octal(v, p, s));
+
+  /*
+   * In non-strict mode, we allow the number to overwrite one or
+   * more bytes of the field termination.  Even old tar
+   * implementations should be able to handle this with no
+   * problem.
+   */
+  if (v >= 0) {
+    while (s <= maxsize) {
+      if (v < limit)
+        return (format_octal(v, p, s));
+      s++;
+      limit <<= 3;
+    }
+  }
+
+  /* Base-256 can handle any number, positive or negative. */
+  return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+  static int
+format_256(int64_t v, char *p, int s)
+{
+  p += s;
+  while (s-- > 0) {
+    *--p = (char)(v & 0xff);
+    v >>= 8;
+  }
+  *p |= 0x80; /* Set the base-256 marker bit. */
+  return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+  static int
+format_octal(int64_t v, char *p, int s)
+{
+  int len;
+
+  len = s;
+
+  /* Octal values can't be negative, so use 0. */
+  if (v < 0) {
+    while (len-- > 0)
+      *p++ = '0';
+    return (-1);
+  }
+
+  p += s;  /* Start at the end and work backwards. */
+  while (s-- > 0) {
+    *--p = '0' + (v & 7);
+    v >>= 3;
+  }
+
+  if (v == 0)
+    return (0);
+
+  /* If it overflowed, fill field with max value. */
+  while (len-- > 0)
+    *p++ = '7';
+
+  return (-1);
+}
+
+  static int
+archive_write_ustar_finish(struct archive *a)
+{
+  struct ustar *ustar;
+  int r;
+
+  r = ARCHIVE_OK;
+  ustar = (struct ustar*)a->format_data;
+  /*
+   * Suppress end-of-archive if nothing else was ever written.
+   * This fixes a problem where setting one format, then another
+   * ends up writing a gratuitous end-of-archive marker.
+   */
+  if (ustar->written && a->compression_write != NULL)
+    r = write_nulls(a, 512*2);
+  free(ustar);
+  a->format_data = NULL;
+  return (r);
+}
+
+  static int
+archive_write_ustar_finish_entry(struct archive *a)
+{
+  struct ustar *ustar;
+  int ret;
+
+  ustar = (struct ustar*) a->format_data;
+  ret = write_nulls(a,
+    ustar->entry_bytes_remaining + ustar->entry_padding);
+  ustar->entry_bytes_remaining = ustar->entry_padding = 0;
+  return (ret);
+}
+
+  static int
+write_nulls(struct archive *a, size_t padding)
+{
+  int ret, to_write;
+
+  while (padding > 0) {
+    to_write = padding < a->null_length ? padding : a->null_length;
+    ret = (a->compression_write)(a, a->nulls, to_write);
+    if (ret != ARCHIVE_OK)
+      return (ret);
+    padding -= to_write;
+  }
+  return (ARCHIVE_OK);
+}
+
+  static int
+archive_write_ustar_data(struct archive *a, const void *buff, size_t s)
+{
+  struct ustar *ustar;
+  int ret;
+
+  ustar = (struct ustar*)a->format_data;
+  if (s > ustar->entry_bytes_remaining)
+    s = ustar->entry_bytes_remaining;
+  ret = (a->compression_write)(a, buff, s);
+  ustar->entry_bytes_remaining -= s;
+  return (ret);
+}
+
+//--- archive_write_set_compression_gzip.c
+
+struct private_data {
+  z_stream  stream;
+  int64_t   total_in;
+  unsigned char *compressed;
+  size_t   compressed_buffer_size;
+  unsigned long  crc;
+};
+
+
+/*
+ * Yuck.  zlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define SET_NEXT_IN(st,src)     \
+  (st)->stream.next_in = (Bytef*)(void *)(uintptr_t)(const void *)(src)
+
+static int archive_compressor_gzip_finish(struct archive *);
+static int archive_compressor_gzip_init(struct archive *);
+static int archive_compressor_gzip_write(struct archive *, const void *,
+  size_t);
+static int drive_compressor(struct archive *, struct private_data *,
+  int finishing);
+
+
+/*
+ * Allocate, initialize and return a archive object.
+ */
+  int
+archive_write_set_compression_gzip(struct archive *a)
+{
+  __archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
+  a->compression_init = &archive_compressor_gzip_init;
+  a->compression_code = ARCHIVE_COMPRESSION_GZIP;
+  a->compression_name = "gzip";
+  return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+  static int
+archive_compressor_gzip_init(struct archive *a)
+{
+  int ret;
+  struct private_data *state;
+  time_t t;
+
+  a->compression_code = ARCHIVE_COMPRESSION_GZIP;
+  a->compression_name = "gzip";
+
+  if (a->client_opener != NULL) {
+    ret = (a->client_opener)(a, a->client_data);
+    if (ret != ARCHIVE_OK)
+      return (ret);
+  }
+
+  state = (struct private_data *)malloc(sizeof(*state));
+  if (state == NULL) {
+    archive_set_error(a, ENOMEM,
+      "Can't allocate data for compression");
+    return (ARCHIVE_FATAL);
+  }
+  memset(state, 0, sizeof(*state));
+
+  state->compressed_buffer_size = a->bytes_per_block;
+  state->compressed = (unsigned char*)malloc(state->compressed_buffer_size);
+  state->crc = crc32(0L, NULL, 0);
+
+  if (state->compressed == NULL) {
+    archive_set_error(a, ENOMEM,
+      "Can't allocate data for compression buffer");
+    free(state);
+    return (ARCHIVE_FATAL);
+  }
+
+  state->stream.next_out = state->compressed;
+  state->stream.avail_out = state->compressed_buffer_size;
+
+  /* Prime output buffer with a gzip header. */
+  t = time(NULL);
+  state->compressed[0] = 0x1f; /* GZip signature bytes */
+  state->compressed[1] = 0x8b;
+  state->compressed[2] = 0x08; /* "Deflate" compression */
+  state->compressed[3] = 0; /* No options */
+  state->compressed[4] = (t)&0xff;  /* Timestamp */
+  state->compressed[5] = (t>>8)&0xff;
+  state->compressed[6] = (t>>16)&0xff;
+  state->compressed[7] = (t>>24)&0xff;
+  state->compressed[8] = 0; /* No deflate options */
+  state->compressed[9] = 3; /* OS=Unix */
+  state->stream.next_out += 10;
+  state->stream.avail_out -= 10;
+
+  a->compression_write = archive_compressor_gzip_write;
+  a->compression_finish = archive_compressor_gzip_finish;
+
+  /* Initialize compression library. */
+  ret = deflateInit2(&(state->stream),
+    Z_DEFAULT_COMPRESSION,
+    Z_DEFLATED,
+    -15 /* < 0 to suppress zlib header */,
+    8,
+    Z_DEFAULT_STRATEGY);
+
+  if (ret == Z_OK) {
+    a->compression_data = state;
+    return (0);
+  }
+
+  /* Library setup failed: clean up. */
+  archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error "
+    "initializing compression library");
+  free(state->compressed);
+  free(state);
+
+  /* Override the error message if we know what really went wrong. */
+  switch (ret) {
+  case Z_STREAM_ERROR:
+    archive_set_error(a, ARCHIVE_ERRNO_MISC,
+      "Internal error initializing "
+      "compression library: invalid setup parameter");
+    break;
+  case Z_MEM_ERROR:
+    archive_set_error(a, ENOMEM, "Internal error initializing "
+      "compression library");
+    break;
+  case Z_VERSION_ERROR:
+    archive_set_error(a, ARCHIVE_ERRNO_MISC,
+      "Internal error initializing "
+      "compression library: invalid library version");
+    break;
+  }
+
+  return (ARCHIVE_FATAL);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+  static int
+archive_compressor_gzip_write(struct archive *a, const void *buff,
+  size_t length)
+{
+  struct private_data *state;
+  int ret;
+
+  state = (struct private_data*)a->compression_data;
+  if (a->client_writer == NULL) {
+    archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+      "No write callback is registered?  "
+      "This is probably an internal programming error.");
+    return (ARCHIVE_FATAL);
+  }
+
+  /* Update statistics */
+  state->crc = crc32(state->crc, (const Bytef*)buff, length);
+  state->total_in += length;
+
+  /* Compress input data to output buffer */
+  SET_NEXT_IN(state, buff);
+  state->stream.avail_in = length;
+  if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
+    return (ret);
+
+  a->file_position += length;
+  return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+  static int
+archive_compressor_gzip_finish(struct archive *a)
+{
+  ssize_t block_length, target_block_length, bytes_written;
+  int ret;
+  struct private_data *state;
+  unsigned tocopy;
+  unsigned char trailer[8];
+
+  state = (struct private_data*)a->compression_data;
+  ret = 0;
+  if (a->client_writer == NULL) {
+    archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+      "No write callback is registered?  "
+      "This is probably an internal programming error.");
+    ret = ARCHIVE_FATAL;
+    goto cleanup;
+  }
+
+  /* By default, always pad the uncompressed data. */
+  if (a->pad_uncompressed) {
+    tocopy = a->bytes_per_block -
+      (state->total_in % a->bytes_per_block);
+    while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) {
+      SET_NEXT_IN(state, a->nulls);
+      state->stream.avail_in = tocopy < a->null_length ?
+        tocopy : a->null_length;
+      state->crc = crc32(state->crc, a->nulls,
+        state->stream.avail_in);
+      state->total_in += state->stream.avail_in;
+      tocopy -= state->stream.avail_in;
+      ret = drive_compressor(a, state, 0);
+      if (ret != ARCHIVE_OK)
+        goto cleanup;
+    }
+  }
+
+  /* Finish compression cycle */
+  if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK)
+    goto cleanup;
+
+  /* Build trailer: 4-byte CRC and 4-byte length. */
+  trailer[0] = (state->crc)&0xff;
+  trailer[1] = (state->crc >> 8)&0xff;
+  trailer[2] = (state->crc >> 16)&0xff;
+  trailer[3] = (state->crc >> 24)&0xff;
+  trailer[4] = (state->total_in)&0xff;
+  trailer[5] = (state->total_in >> 8)&0xff;
+  trailer[6] = (state->total_in >> 16)&0xff;
+  trailer[7] = (state->total_in >> 24)&0xff;
+
+  /* Add trailer to current block. */
+  tocopy = 8;
+  if (tocopy > state->stream.avail_out)
+    tocopy = state->stream.avail_out;
+  memcpy(state->stream.next_out, trailer, tocopy);
+  state->stream.next_out += tocopy;
+  state->stream.avail_out -= tocopy;
+
+  /* If it overflowed, flush and start a new block. */
+  if (tocopy < 8) {
+    bytes_written = (a->client_writer)(a, a->client_data,
+      state->compressed, state->compressed_buffer_size);
+    if (bytes_written <= 0) {
+      ret = ARCHIVE_FATAL;
+      goto cleanup;
+    }
+    a->raw_position += bytes_written;
+    state->stream.next_out = state->compressed;
+    state->stream.avail_out = state->compressed_buffer_size;
+    memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
+    state->stream.next_out += 8-tocopy;
+    state->stream.avail_out -= 8-tocopy;
+  }
+
+  /* Optionally, pad the final compressed block. */
+  block_length = state->stream.next_out - state->compressed;
+
+
+  /* Tricky calculation to determine size of last block. */
+  target_block_length = block_length;
+  if (a->bytes_in_last_block <= 0)
+    /* Default or Zero: pad to full block */
+    target_block_length = a->bytes_per_block;
+  else
+    /* Round length to next multiple of bytes_in_last_block. */
+    target_block_length = a->bytes_in_last_block *
+      ( (block_length + a->bytes_in_last_block - 1) /
+        a->bytes_in_last_block);
+  if (target_block_length > a->bytes_per_block)
+    target_block_length = a->bytes_per_block;
+  if (block_length < target_block_length) {
+    memset(state->stream.next_out, 0,
+      target_block_length - block_length);
+    block_length = target_block_length;
+  }
+
+  /* Write the last block */
+  bytes_written = (a->client_writer)(a, a->client_data,
+    state->compressed, block_length);
+  if (bytes_written <= 0) {
+    ret = ARCHIVE_FATAL;
+    goto cleanup;
+  }
+  a->raw_position += bytes_written;
+
+  /* Cleanup: shut down compressor, release memory, etc. */
+cleanup:
+  switch (deflateEnd(&(state->stream))) {
+  case Z_OK:
+    break;
+  default:
+    archive_set_error(a, ARCHIVE_ERRNO_MISC,
+      "Failed to clean up compressor");
+    ret = ARCHIVE_FATAL;
+  }
+  free(state->compressed);
+  free(state);
+
+  /* Close the output */
+  if (a->client_closer != NULL)
+    (a->client_closer)(a, a->client_data);
+
+  return (ret);
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+  static int
+drive_compressor(struct archive *a, struct private_data *state, int finishing)
+{
+  ssize_t bytes_written;
+  int ret;
+
+  for (;;) {
+    if (state->stream.avail_out == 0) {
+      bytes_written = (a->client_writer)(a, a->client_data,
+        state->compressed, state->compressed_buffer_size);
+      if (bytes_written <= 0) {
+        /* TODO: Handle this write failure */
+        return (ARCHIVE_FATAL);
+      } else if ((size_t)bytes_written < state->compressed_buffer_size) {
+        /* Short write: Move remaining to
+         * front of block and keep filling */
+        memmove(state->compressed,
+          state->compressed + bytes_written,
+          state->compressed_buffer_size - bytes_written);
+      }
+      a->raw_position += bytes_written;
+      state->stream.next_out
+        = state->compressed +
+        state->compressed_buffer_size - bytes_written;
+      state->stream.avail_out = bytes_written;
+    }
+
+    ret = deflate(&(state->stream),
+      finishing ? Z_FINISH : Z_NO_FLUSH );
+
+    switch (ret) {
+    case Z_OK:
+      /* In non-finishing case, check if compressor
+       * consumed everything */
+      if (!finishing && state->stream.avail_in == 0)
+        return (ARCHIVE_OK);
+      /* In finishing case, this return always means
+       * there's more work */
+      break;
+    case Z_STREAM_END:
+      /* This return can only occur in finishing case. */
+      return (ARCHIVE_OK);
+    default:
+      /* Any other return value indicates an error. */
+      archive_set_error(a, ARCHIVE_ERRNO_MISC,
+        "GZip compression failed");
+      return (ARCHIVE_FATAL);
+    }
+  }
+}
+

+ 64 - 0
Source/CPack/cmCPackTGZGenerator.h

@@ -0,0 +1,64 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc. All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackTGZGenerator_h
+#define cmCPackTGZGenerator_h
+
+
+#include "cmCPackGenericGenerator.h"
+#include "CPack/cmCPackConfigure.h" // for ssize_t
+
+/** \class cmCPackTGZGenerator
+ * \brief A generator for TGZ files
+ *
+ * http://people.freebsd.org/~kientzle/libarchive/
+ */
+class cmCPackTGZGenerator : public cmCPackGenericGenerator
+{
+public:
+  cmCPackTypeMacro(cmCPackTGZGenerator, cmCPackGenericGenerator);
+
+  /**
+   * Do the actual processing. Subclass has to override it.
+   * Return < 0 if error.
+   */
+  virtual int ProcessGenerator();
+
+  /**
+   * Initialize generator
+   */
+  virtual int Initialize(const char* name);
+
+  /**
+   * Construct generator
+   */
+  cmCPackTGZGenerator();
+  virtual ~cmCPackTGZGenerator();
+
+protected:
+  static int TGZ_Open(struct archive *a, void *client_data);
+  static ssize_t TGZ_Write(struct archive *a, void *client_data, void *buff, size_t n);
+  static int TGZ_Close(struct archive *a, void *client_data);
+
+  int CompressFiles(const char* outFileName, const char* toplevel,
+    const std::vector<std::string>& files);
+  virtual const char* GetOutputExtension() { return "tar.gz"; }
+};
+
+#endif
+
+

+ 260 - 0
Source/CPack/cpack.cxx

@@ -0,0 +1,260 @@
+/*=========================================================================
+
+  Program:   CMake - Cross-Platform Makefile Generator
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Kitware, Inc., Insight Consortium.  All rights reserved.
+  See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmSystemTools.h"
+
+// Need these for documentation support.
+#include "cmake.h"
+#include "cmDocumentation.h"
+#include "cmCPackGenerators.h"
+#include "cmCPackGenericGenerator.h"
+
+#include <cmsys/CommandLineArguments.hxx>
+
+//----------------------------------------------------------------------------
+static const cmDocumentationEntry cmDocumentationName[] =
+{
+  {0,
+   "  cpack - Packaging driver provided by CMake.", 0},
+  {0,0,0}
+};
+
+//----------------------------------------------------------------------------
+static const cmDocumentationEntry cmDocumentationUsage[] =
+{
+  {0,
+   "  cpack -G <generator> -P <ProjectName> -R <ReleaseVersion> [options]", 0},
+  {0,0,0}
+};
+
+//----------------------------------------------------------------------------
+static const cmDocumentationEntry cmDocumentationDescription[] =
+{
+  {0,
+   "The \"cpack\" executable is the CMake packaging program.  "
+   "CMake-generated build trees created for projects that use "
+   "the INSTALL_* commands have packaging support.  "
+   "This program will generate the package.", 0},
+  CMAKE_STANDARD_INTRODUCTION,
+  {0,0,0}
+};
+
+//----------------------------------------------------------------------------
+static const cmDocumentationEntry cmDocumentationOptions[] =
+{
+    {"-G <generator>", "Use the specified generator to generate package.",
+    "CPack may support multiple native packaging systems on certain platforms. A "
+      "generator is responsible for generating input files for particular system "
+      "and invoking that systems. Possible generator names are specified in the "
+      "Generators section." },
+    {"-P <ProjectName>", "Specify the project name.",
+    "This option specifies the project name that will be used to generate the "
+      "installer." },
+    {"-R <ReleaseVersion>", "Specify the release version of the project.",
+    "This option specifies the release version of the project that will be "
+      "used by installer." },
+    {"-D <var>=<value>", "Set a CPack variable.", \
+    "Set a variable that can be used by the generator."}, \
+    {"--patch <ReleasePatch>", "Specify the patch of the project.",
+    "This option specifies the patch of the project that will be "
+      "used by installer." },
+    {"--vendor <ProjectVendor>", "Specify the vendor of the project.",
+    "This option specifies the vendor of the project that will be "
+      "used by installer." },
+    {0,0,0}
+};
+
+//----------------------------------------------------------------------------
+static const cmDocumentationEntry cmDocumentationSeeAlso[] =
+{
+    {0, "cmake", 0},
+    {0, "ccmake", 0},
+    {0, 0, 0}
+};
+
+//----------------------------------------------------------------------------
+int cpackUnknownArgument(const char*, void*)
+{
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+typedef std::map<cmStdString, cmStdString> cpackDefinitionsMapType;
+
+//----------------------------------------------------------------------------
+int cpackDefinitionArgument(const char* argument, const char* cValue, 
+    void* call_data)
+{
+  (void)argument;
+  std::string value = cValue;
+  size_t pos = value.find_first_of("=");
+  if ( pos == std::string::npos )
+    {
+    std::cerr << "Please specify CPack definitions as: KEY=VALUE" << std::endl;
+    return 0;
+    }
+  std::string key = value.substr(0, pos);
+  value = value.c_str() + pos + 1;
+  cpackDefinitionsMapType* map = static_cast<cpackDefinitionsMapType*>(call_data);
+  (*map)[key] = value;
+  return 1;
+}
+
+//----------------------------------------------------------------------------
+// this is CPack.
+int main (int argc, char *argv[])
+{
+  int res = 0;
+  cmSystemTools::EnableMSVCDebugHook();
+
+  if ( cmSystemTools::GetCurrentWorkingDirectory().size() == 0 )
+    {
+    std::cerr << "Current working directory cannot be established." << std::endl;
+    }
+
+  std::string generator;
+  bool help = false;
+  bool helpVersion = false;
+  std::string helpFull;
+  std::string helpMAN;
+  std::string helpHTML;
+
+  std::string cpackProjectName;
+  std::string cpackProjectDirectory = cmsys::SystemTools::GetCurrentWorkingDirectory();
+  std::string cpackBuildConfig;
+  std::string cpackProjectVersion;
+  std::string cpackProjectPatch;
+  std::string cpackProjectVendor;
+  cpackDefinitionsMapType definitionsMap;
+
+  cmDocumentation doc;
+  cmsys::CommandLineArguments arg;
+  arg.Initialize(argc, argv);
+  typedef cmsys::CommandLineArguments argT;
+  // Help arguments
+  arg.AddArgument("--help", argT::NO_ARGUMENT, &help, "CPack help");
+  arg.AddArgument("--help-full", argT::SPACE_ARGUMENT, &helpFull, "CPack help");
+  arg.AddArgument("--help-html", argT::SPACE_ARGUMENT, &helpHTML, "CPack help");
+  arg.AddArgument("--help-man", argT::SPACE_ARGUMENT, &helpMAN, "CPack help");
+  arg.AddArgument("--version", argT::NO_ARGUMENT, &helpVersion, "CPack help");
+
+  arg.AddArgument("-C", argT::SPACE_ARGUMENT, &cpackBuildConfig, "CPack build configuration");
+  arg.AddArgument("-G", argT::SPACE_ARGUMENT, &generator, "CPack generator");
+  arg.AddArgument("-P", argT::SPACE_ARGUMENT, &cpackProjectName, "CPack project name");
+  arg.AddArgument("-R", argT::SPACE_ARGUMENT, &cpackProjectVersion, "CPack project version");
+  arg.AddArgument("-B", argT::SPACE_ARGUMENT, &cpackProjectDirectory, "CPack project directory");
+  arg.AddArgument("--patch", argT::SPACE_ARGUMENT, &cpackProjectPatch, "CPack project patch");
+  arg.AddArgument("--vendor", argT::SPACE_ARGUMENT, &cpackProjectVendor, "CPack project vendor");
+  arg.AddCallback("-D", argT::SPACE_ARGUMENT, cpackDefinitionArgument, &definitionsMap, "CPack Definitions");
+  arg.SetUnknownArgumentCallback(cpackUnknownArgument);
+
+  int parsed = arg.Parse();
+
+  cmCPackGenerators generators;
+  cmCPackGenericGenerator* cpackGenerator = 0;
+
+  if ( !helpFull.empty() || !helpMAN.empty() || !helpHTML.empty() || helpVersion )
+    {
+    help = true;
+    }
+
+  if ( parsed && !help )
+    {
+    if ( generator.empty() )
+      {
+      std::cerr << "CPack generator not specified" << std::endl;
+      parsed = 0;
+      }
+    if ( parsed && cpackProjectName.empty() )
+      {
+      std::cerr << "CPack project name not specified" << std::endl;
+      parsed = 0;
+      }
+    if ( parsed && cpackProjectVersion.empty() )
+      {
+      std::cerr << "CPack project version not specified" << std::endl;
+      parsed = 0;
+      }
+    if ( parsed )
+      {
+      cpackGenerator = generators.NewGenerator(generator.c_str());
+      if ( !cpackGenerator )
+        {
+        std::cerr << "Cannot initialize CPack generator: " << generator.c_str() << std::endl;
+        parsed = 0;
+        }
+      if ( parsed && !cpackGenerator->FindRunningCMake(argv[0]) )
+        {
+        std::cerr << "Cannot initialize the generator" << std::endl;
+        parsed = 0;
+        }
+
+      cmsys::SystemTools::ConvertToUnixSlashes(cpackProjectDirectory);
+      std::string makeInstallFile = cpackProjectDirectory + "/cmake_install.cmake";
+      if ( !cmsys::SystemTools::FileExists(makeInstallFile.c_str()) )
+        {
+        std::cerr << "Cannot find installation file: " << makeInstallFile.c_str() << std::endl;
+        parsed = 0;
+        }
+      }
+    }
+
+  if ( !parsed || help )
+    {
+    doc.CheckOptions(argc, argv);
+    // Construct and print requested documentation.
+    doc.SetName("cpack");
+    doc.SetNameSection(cmDocumentationName);
+    doc.SetUsageSection(cmDocumentationUsage);
+    doc.SetDescriptionSection(cmDocumentationDescription);
+    doc.SetOptionsSection(cmDocumentationOptions);
+    doc.SetSeeAlsoList(cmDocumentationSeeAlso);
+    return doc.PrintRequestedDocumentation(std::cout)? 0:1;
+    }
+
+#ifdef _WIN32
+  std::string comspec = "cmw9xcom.exe";
+  cmSystemTools::SetWindows9xComspecSubstitute(comspec.c_str());
+#endif
+
+  std::cout << "Use generator: " << cpackGenerator->GetNameOfClass() << std::endl;
+  std::cout << "For project: " << cpackProjectName.c_str() << std::endl;
+  cpackGenerator->SetOption("CPACK_PROJECT_NAME", cpackProjectName.c_str());
+  cpackGenerator->SetOption("CPACK_PROJECT_VERSION", cpackProjectVersion.c_str());
+  cpackGenerator->SetOption("CPACK_PROJECT_VERSION_PATCH", cpackProjectPatch.c_str());
+  cpackGenerator->SetOption("CPACK_PROJECT_VENDOR", cpackProjectVendor.c_str());
+  cpackGenerator->SetOption("CPACK_PROJECT_DIRECTORY", cpackProjectDirectory.c_str());
+  if ( !cpackBuildConfig.empty() )
+    {
+    cpackGenerator->SetOption("CPACK_BUILD_CONFIG", cpackBuildConfig.c_str());
+    }
+  cpackDefinitionsMapType::iterator cdit;
+  for ( cdit = definitionsMap.begin(); cdit != definitionsMap.end(); ++cdit )
+    {
+    cpackGenerator->SetOption(cdit->first.c_str(), cdit->second.c_str());
+    }
+
+  res = cpackGenerator->ProcessGenerator();
+  if ( !res )
+    {
+    std::cerr << "Error when generating package: " << cpackProjectName.c_str() << std::endl;
+    return 1;
+    }
+
+  return 0;
+}
+
+