// ZipFile.AddUpdate.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009-2011 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2011-November-01 13:56:58>
//
// ------------------------------------------------------------------
//
// This module defines the methods for Adding and Updating entries in
// the ZipFile.
//
// ------------------------------------------------------------------
//


using System;
using System.IO;
using System.Collections.Generic;

namespace OfficeOpenXml.Packaging.Ionic.Zip
{
    internal partial class ZipFile
    {
        /// <summary>
        ///   Adds an item, either a file or a directory, to a zip file archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method is handy if you are adding things to zip archive and don't
        ///   want to bother distinguishing between directories or files.  Any files are
        ///   added as single entries.  A directory added through this method is added
        ///   recursively: all files and subdirectories contained within the directory
        ///   are added to the <c>ZipFile</c>.
        /// </para>
        ///
        /// <para>
        ///   The name of the item may be a relative path or a fully-qualified
        ///   path. Remember, the items contained in <c>ZipFile</c> instance get written
        ///   to the disk only when you call <see cref="ZipFile.Save()"/> or a similar
        ///   save method.
        /// </para>
        ///
        /// <para>
        ///   The directory name used for the file within the archive is the same
        ///   as the directory name (potentially a relative path) specified in the
        ///   <paramref name="fileOrDirectoryName"/>.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
        ///
        /// <overloads>This method has two overloads.</overloads>
        /// <param name="fileOrDirectoryName">
        /// the name of the file or directory to add.</param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddItem(string fileOrDirectoryName)
        {
            return AddItem(fileOrDirectoryName, null);
        }


        /// <summary>
        ///   Adds an item, either a file or a directory, to a zip file archive,
        ///   explicitly specifying the directory path to be used in the archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   If adding a directory, the add is recursive on all files and
        ///   subdirectories contained within it.
        /// </para>
        /// <para>
        ///   The name of the item may be a relative path or a fully-qualified path.
        ///   The item added by this call to the <c>ZipFile</c> is not read from the
        ///   disk nor written to the zip file archive until the application calls
        ///   Save() on the <c>ZipFile</c>.
        /// </para>
        ///
        /// <para>
        ///   This version of the method allows the caller to explicitly specify the
        ///   directory path to be used in the archive, which would override the
        ///   "natural" path of the filesystem file.
        /// </para>
        ///
        /// <para>
        ///   Encryption will be used on the file data if the <c>Password</c> has
        ///   been set on the <c>ZipFile</c> object, prior to calling this method.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <exception cref="System.IO.FileNotFoundException">
        ///   Thrown if the file or directory passed in does not exist.
        /// </exception>
        ///
        /// <param name="fileOrDirectoryName">the name of the file or directory to add.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   The name of the directory path to use within the zip archive.  This path
        ///   need not refer to an extant directory in the current filesystem.  If the
        ///   files within the zip are later extracted, this is the path used for the
        ///   extracted file.  Passing <c>null</c> (<c>Nothing</c> in VB) will use the
        ///   path on the fileOrDirectoryName.  Passing the empty string ("") will
        ///   insert the item at the root path within the archive.
        /// </param>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string, string)"/>
        ///
        /// <example>
        ///   This example shows how to zip up a set of files into a flat hierarchy,
        ///   regardless of where in the filesystem the files originated. The resulting
        ///   zip archive will contain a toplevel directory named "flat", which itself
        ///   will contain files Readme.txt, MyProposal.docx, and Image1.jpg.  A
        ///   subdirectory under "flat" called SupportFiles will contain all the files
        ///   in the "c:\SupportFiles" directory on disk.
        ///
        /// <code>
        /// String[] itemnames= {
        ///   "c:\\fixedContent\\Readme.txt",
        ///   "MyProposal.docx",
        ///   "c:\\SupportFiles",  // a directory
        ///   "images\\Image1.jpg"
        /// };
        ///
        /// try
        /// {
        ///   using (ZipFile zip = new ZipFile())
        ///   {
        ///     for (int i = 1; i &lt; itemnames.Length; i++)
        ///     {
        ///       // will add Files or Dirs, recurses and flattens subdirectories
        ///       zip.AddItem(itemnames[i],"flat");
        ///     }
        ///     zip.Save(ZipToCreate);
        ///   }
        /// }
        /// catch (System.Exception ex1)
        /// {
        ///   System.Console.Error.WriteLine("exception: {0}", ex1);
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        ///   Dim itemnames As String() = _
        ///     New String() { "c:\fixedContent\Readme.txt", _
        ///                    "MyProposal.docx", _
        ///                    "SupportFiles", _
        ///                    "images\Image1.jpg" }
        ///   Try
        ///       Using zip As New ZipFile
        ///           Dim i As Integer
        ///           For i = 1 To itemnames.Length - 1
        ///               ' will add Files or Dirs, recursing and flattening subdirectories.
        ///               zip.AddItem(itemnames(i), "flat")
        ///           Next i
        ///           zip.Save(ZipToCreate)
        ///       End Using
        ///   Catch ex1 As Exception
        ///       Console.Error.WriteLine("exception: {0}", ex1.ToString())
        ///   End Try
        /// </code>
        /// </example>
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddItem(String fileOrDirectoryName, String directoryPathInArchive)
        {
            if (File.Exists(fileOrDirectoryName))
                return AddFile(fileOrDirectoryName, directoryPathInArchive);

            if (Directory.Exists(fileOrDirectoryName))
                return AddDirectory(fileOrDirectoryName, directoryPathInArchive);

            throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!",
                                                          fileOrDirectoryName));
        }

        /// <summary>
        ///   Adds a File to a Zip file archive.
        /// </summary>
        /// <remarks>
        ///
        /// <para>
        ///   This call collects metadata for the named file in the filesystem,
        ///   including the file attributes and the timestamp, and inserts that metadata
        ///   into the resulting ZipEntry.  Only when the application calls Save() on
        ///   the <c>ZipFile</c>, does DotNetZip read the file from the filesystem and
        ///   then write the content to the zip file archive.
        /// </para>
        ///
        /// <para>
        ///   This method will throw an exception if an entry with the same name already
        ///   exists in the <c>ZipFile</c>.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <para>
        ///   In this example, three files are added to a Zip archive. The ReadMe.txt
        ///   file will be placed in the root of the archive. The .png file will be
        ///   placed in a folder within the zip called photos\personal.  The pdf file
        ///   will be included into a folder within the zip called Desktop.
        /// </para>
        /// <code>
        ///    try
        ///    {
        ///      using (ZipFile zip = new ZipFile())
        ///      {
        ///        zip.AddFile("c:\\photos\\personal\\7440-N49th.png");
        ///        zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf");
        ///        zip.AddFile("ReadMe.txt");
        ///
        ///        zip.Save("Package.zip");
        ///      }
        ///    }
        ///    catch (System.Exception ex1)
        ///    {
        ///      System.Console.Error.WriteLine("exception: " + ex1);
        ///    }
        /// </code>
        ///
        /// <code lang="VB">
        ///  Try
        ///       Using zip As ZipFile = New ZipFile
        ///           zip.AddFile("c:\photos\personal\7440-N49th.png")
        ///           zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf")
        ///           zip.AddFile("ReadMe.txt")
        ///           zip.Save("Package.zip")
        ///       End Using
        ///   Catch ex1 As Exception
        ///       Console.Error.WriteLine("exception: {0}", ex1.ToString)
        ///   End Try
        /// </code>
        /// </example>
        ///
        /// <overloads>This method has two overloads.</overloads>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
        ///
        /// <param name="fileName">
        ///   The name of the file to add. It should refer to a file in the filesystem.
        ///   The name of the file may be a relative path or a fully-qualified path.
        /// </param>
        /// <returns>The <c>ZipEntry</c> corresponding to the File added.</returns>
        public ZipEntry AddFile(string fileName)
        {
            return AddFile(fileName, null);
        }





        /// <summary>
        ///   Adds a File to a Zip file archive, potentially overriding the path to be
        ///   used within the zip archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The file added by this call to the <c>ZipFile</c> is not written to the
        ///   zip file archive until the application calls Save() on the <c>ZipFile</c>.
        /// </para>
        ///
        /// <para>
        ///   This method will throw an exception if an entry with the same name already
        ///   exists in the <c>ZipFile</c>.
        /// </para>
        ///
        /// <para>
        ///   This version of the method allows the caller to explicitly specify the
        ///   directory path to be used in the archive.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <para>
        ///   In this example, three files are added to a Zip archive. The ReadMe.txt
        ///   file will be placed in the root of the archive. The .png file will be
        ///   placed in a folder within the zip called images.  The pdf file will be
        ///   included into a folder within the zip called files\docs, and will be
        ///   encrypted with the given password.
        /// </para>
        /// <code>
        /// try
        /// {
        ///   using (ZipFile zip = new ZipFile())
        ///   {
        ///     // the following entry will be inserted at the root in the archive.
        ///     zip.AddFile("c:\\datafiles\\ReadMe.txt", "");
        ///     // this image file will be inserted into the "images" directory in the archive.
        ///     zip.AddFile("c:\\photos\\personal\\7440-N49th.png", "images");
        ///     // the following will result in a password-protected file called
        ///     // files\\docs\\2008-Regional-Sales-Report.pdf  in the archive.
        ///     zip.Password = "EncryptMe!";
        ///     zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf", "files\\docs");
        ///     zip.Save("Archive.zip");
        ///   }
        /// }
        /// catch (System.Exception ex1)
        /// {
        ///   System.Console.Error.WriteLine("exception: {0}", ex1);
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        ///   Try
        ///       Using zip As ZipFile = New ZipFile
        ///           ' the following entry will be inserted at the root in the archive.
        ///           zip.AddFile("c:\datafiles\ReadMe.txt", "")
        ///           ' this image file will be inserted into the "images" directory in the archive.
        ///           zip.AddFile("c:\photos\personal\7440-N49th.png", "images")
        ///           ' the following will result in a password-protected file called
        ///           ' files\\docs\\2008-Regional-Sales-Report.pdf  in the archive.
        ///           zip.Password = "EncryptMe!"
        ///           zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf", "files\documents")
        ///           zip.Save("Archive.zip")
        ///       End Using
        ///   Catch ex1 As Exception
        ///       Console.Error.WriteLine("exception: {0}", ex1)
        ///   End Try
        /// </code>
        /// </example>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string,string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
        ///
        /// <param name="fileName">
        ///   The name of the file to add.  The name of the file may be a relative path
        ///   or a fully-qualified path.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the fileName.
        ///   This path may, or may not, correspond to a real directory in the current
        ///   filesystem.  If the files within the zip are later extracted, this is the
        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
        ///   VB) will use the path on the fileName, if any.  Passing the empty string
        ///   ("") will insert the item at the root path within the archive.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> corresponding to the file added.</returns>
        public ZipEntry AddFile(string fileName, String directoryPathInArchive)
        {
            string nameInArchive = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
            ZipEntry ze = ZipEntry.CreateFromFile(fileName, nameInArchive);
            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", fileName);
            return _InternalAddEntry(ze);
        }


        /// <summary>
        ///   This method removes a collection of entries from the <c>ZipFile</c>.
        /// </summary>
        ///
        /// <param name="entriesToRemove">
        ///   A collection of ZipEntry instances from this zip file to be removed. For
        ///   example, you can pass in an array of ZipEntry instances; or you can call
        ///   SelectEntries(), and then add or remove entries from that
        ///   ICollection&lt;ZipEntry&gt; (ICollection(Of ZipEntry) in VB), and pass
        ///   that ICollection to this method.
        /// </param>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
        /// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
        public void RemoveEntries(System.Collections.Generic.ICollection<ZipEntry> entriesToRemove)
        {
            if (entriesToRemove == null)
                throw new ArgumentNullException("entriesToRemove");

            foreach (ZipEntry e in entriesToRemove)
            {
                this.RemoveEntry(e);
            }
        }


        /// <summary>
        ///   This method removes a collection of entries from the <c>ZipFile</c>, by name.
        /// </summary>
        ///
        /// <param name="entriesToRemove">
        ///   A collection of strings that refer to names of entries to be removed
        ///   from the <c>ZipFile</c>.  For example, you can pass in an array or a
        ///   List of Strings that provide the names of entries to be removed.
        /// </param>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.SelectEntries(String)" />
        /// <seealso cref="Ionic.Zip.ZipFile.RemoveSelectedEntries(String)" />
        public void RemoveEntries(System.Collections.Generic.ICollection<String> entriesToRemove)
        {
            if (entriesToRemove == null)
                throw new ArgumentNullException("entriesToRemove");

            foreach (String e in entriesToRemove)
            {
                this.RemoveEntry(e);
            }
        }


        /// <summary>
        ///   This method adds a set of files to the <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Use this method to add a set of files to the zip archive, in one call.
        ///   For example, a list of files received from
        ///   <c>System.IO.Directory.GetFiles()</c> can be added to a zip archive in one
        ///   call.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        /// </remarks>
        ///
        /// <param name="fileNames">
        ///   The collection of names of the files to add. Each string should refer to a
        ///   file in the filesystem. The name of the file may be a relative path or a
        ///   fully-qualified path.
        /// </param>
        ///
        /// <example>
        ///   This example shows how to create a zip file, and add a few files into it.
        /// <code>
        /// String ZipFileToCreate = "archive1.zip";
        /// String DirectoryToZip = "c:\\reports";
        /// using (ZipFile zip = new ZipFile())
        /// {
        ///   // Store all files found in the top level directory, into the zip archive.
        ///   String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip);
        ///   zip.AddFiles(filenames);
        ///   zip.Save(ZipFileToCreate);
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        /// Dim ZipFileToCreate As String = "archive1.zip"
        /// Dim DirectoryToZip As String = "c:\reports"
        /// Using zip As ZipFile = New ZipFile
        ///     ' Store all files found in the top level directory, into the zip archive.
        ///     Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip)
        ///     zip.AddFiles(filenames)
        ///     zip.Save(ZipFileToCreate)
        /// End Using
        /// </code>
        /// </example>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames)
        {
            this.AddFiles(fileNames, null);
        }


        /// <summary>
        ///   Adds or updates a set of files in the <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Any files that already exist in the archive are updated. Any files that
        ///   don't yet exist in the archive are added.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        /// </remarks>
        ///
        /// <param name="fileNames">
        ///   The collection of names of the files to update. Each string should refer to a file in
        ///   the filesystem. The name of the file may be a relative path or a fully-qualified path.
        /// </param>
        ///
        public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames)
        {
            this.UpdateFiles(fileNames, null);
        }


        /// <summary>
        ///   Adds a set of files to the <c>ZipFile</c>, using the
        ///   specified directory path in the archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Any directory structure that may be present in the
        ///   filenames contained in the list is "flattened" in the
        ///   archive.  Each file in the list is added to the archive in
        ///   the specified top-level directory.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see
        ///   cref="Encryption"/>, <see cref="Password"/>, <see
        ///   cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see
        ///   cref="ExtractExistingFile"/>, <see
        ///   cref="ZipErrorAction"/>, and <see
        ///   cref="CompressionLevel"/>, their respective values at the
        ///   time of this call will be applied to each ZipEntry added.
        /// </para>
        /// </remarks>
        ///
        /// <param name="fileNames">
        ///   The names of the files to add. Each string should refer to
        ///   a file in the filesystem.  The name of the file may be a
        ///   relative path or a fully-qualified path.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the file name.
        ///   Th is path may, or may not, correspond to a real directory in the current
        ///   filesystem.  If the files within the zip are later extracted, this is the
        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
        ///   the empty string ("") will insert the item at the root path within the
        ///   archive.
        /// </param>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
        {
            AddFiles(fileNames, false, directoryPathInArchive);
        }



        /// <summary>
        ///   Adds a set of files to the <c>ZipFile</c>, using the specified directory
        ///   path in the archive, and preserving the full directory structure in the
        ///   filenames.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   Think of the <paramref name="directoryPathInArchive"/> as a "root" or
        ///   base directory used in the archive for the files that get added.  when
        ///   <paramref name="preserveDirHierarchy"/> is true, the hierarchy of files
        ///   found in the filesystem will be placed, with the hierarchy intact,
        ///   starting at that root in the archive. When <c>preserveDirHierarchy</c>
        ///   is false, the path hierarchy of files is flattned, and the flattened
        ///   set of files gets placed in the root within the archive as specified in
        ///   <c>directoryPathInArchive</c>.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="fileNames">
        ///   The names of the files to add. Each string should refer to a file in the
        ///   filesystem.  The name of the file may be a relative path or a
        ///   fully-qualified path.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use as a prefix for each entry name.
        ///   This path may, or may not, correspond to a real directory in the current
        ///   filesystem.  If the files within the zip are later extracted, this is the
        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
        ///   the empty string ("") will insert the item at the root path within the
        ///   archive.
        /// </param>
        ///
        /// <param name="preserveDirHierarchy">
        ///   whether the entries in the zip archive will reflect the directory
        ///   hierarchy that is present in the various filenames.  For example, if
        ///   <paramref name="fileNames"/> includes two paths,
        ///   \Animalia\Chordata\Mammalia\Info.txt and
        ///   \Plantae\Magnoliophyta\Dicotyledon\Info.txt, then calling this method
        ///   with <paramref name="preserveDirHierarchy"/> = <c>false</c> will
        ///   result in an exception because of a duplicate entry name, while
        ///   calling this method with <paramref name="preserveDirHierarchy"/> =
        ///   <c>true</c> will result in the full direcory paths being included in
        ///   the entries added to the ZipFile.
        /// </param>
        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
        public void AddFiles(System.Collections.Generic.IEnumerable<String> fileNames,
                             bool preserveDirHierarchy,
                             String directoryPathInArchive)
        {
            if (fileNames == null)
                throw new ArgumentNullException("fileNames");

            _addOperationCanceled = false;
            OnAddStarted();
            if (preserveDirHierarchy)
            {
                foreach (var f in fileNames)
                {
                    if (_addOperationCanceled) break;
                    if (directoryPathInArchive != null)
                    {
                        //string s = SharedUtilities.NormalizePath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
                        string s = Path.GetFullPath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f)));
                        this.AddFile(f, s);
                    }
                    else
                        this.AddFile(f, null);
                }
            }
            else
            {
                foreach (var f in fileNames)
                {
                    if (_addOperationCanceled) break;
                    this.AddFile(f, directoryPathInArchive);
                }
            }
            if (!_addOperationCanceled)
                OnAddCompleted();
        }


        /// <summary>
        ///   Adds or updates a set of files to the <c>ZipFile</c>, using the specified
        ///   directory path in the archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   Any files that already exist in the archive are updated. Any files that
        ///   don't yet exist in the archive are added.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        /// </remarks>
        ///
        /// <param name="fileNames">
        ///   The names of the files to add or update. Each string should refer to a
        ///   file in the filesystem.  The name of the file may be a relative path or a
        ///   fully-qualified path.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the file name.
        ///   This path may, or may not, correspond to a real directory in the current
        ///   filesystem.  If the files within the zip are later extracted, this is the
        ///   path used for the extracted file.  Passing <c>null</c> (<c>Nothing</c> in
        ///   VB) will use the path on each of the <c>fileNames</c>, if any.  Passing
        ///   the empty string ("") will insert the item at the root path within the
        ///   archive.
        /// </param>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddSelectedFiles(String, String)" />
        public void UpdateFiles(System.Collections.Generic.IEnumerable<String> fileNames, String directoryPathInArchive)
        {
            if (fileNames == null)
                throw new ArgumentNullException("fileNames");

            OnAddStarted();
            foreach (var f in fileNames)
                this.UpdateFile(f, directoryPathInArchive);
            OnAddCompleted();
        }




        /// <summary>
        ///   Adds or Updates a File in a Zip file archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method adds a file to a zip archive, or, if the file already exists
        ///   in the zip archive, this method Updates the content of that given filename
        ///   in the zip archive.  The <c>UpdateFile</c> method might more accurately be
        ///   called "AddOrUpdateFile".
        /// </para>
        ///
        /// <para>
        ///   Upon success, there is no way for the application to learn whether the file
        ///   was added versus updated.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        /// </remarks>
        ///
        /// <example>
        ///
        ///   This example shows how to Update an existing entry in a zipfile. The first
        ///   call to UpdateFile adds the file to the newly-created zip archive.  The
        ///   second call to UpdateFile updates the content for that file in the zip
        ///   archive.
        ///
        /// <code>
        /// using (ZipFile zip1 = new ZipFile())
        /// {
        ///   // UpdateFile might more accurately be called "AddOrUpdateFile"
        ///   zip1.UpdateFile("MyDocuments\\Readme.txt");
        ///   zip1.UpdateFile("CustomerList.csv");
        ///   zip1.Comment = "This zip archive has been created.";
        ///   zip1.Save("Content.zip");
        /// }
        ///
        /// using (ZipFile zip2 = ZipFile.Read("Content.zip"))
        /// {
        ///   zip2.UpdateFile("Updates\\Readme.txt");
        ///   zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed.";
        ///   zip2.Save();
        /// }
        ///
        /// </code>
        /// <code lang="VB">
        ///   Using zip1 As New ZipFile
        ///       ' UpdateFile might more accurately be called "AddOrUpdateFile"
        ///       zip1.UpdateFile("MyDocuments\Readme.txt")
        ///       zip1.UpdateFile("CustomerList.csv")
        ///       zip1.Comment = "This zip archive has been created."
        ///       zip1.Save("Content.zip")
        ///   End Using
        ///
        ///   Using zip2 As ZipFile = ZipFile.Read("Content.zip")
        ///       zip2.UpdateFile("Updates\Readme.txt")
        ///       zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed."
        ///       zip2.Save
        ///   End Using
        /// </code>
        /// </example>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
        ///
        /// <param name="fileName">
        ///   The name of the file to add or update. It should refer to a file in the
        ///   filesystem.  The name of the file may be a relative path or a
        ///   fully-qualified path.
        /// </param>
        ///
        /// <returns>
        ///   The <c>ZipEntry</c> corresponding to the File that was added or updated.
        /// </returns>
        public ZipEntry UpdateFile(string fileName)
        {
            return UpdateFile(fileName, null);
        }



        /// <summary>
        ///   Adds or Updates a File in a Zip file archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method adds a file to a zip archive, or, if the file already exists
        ///   in the zip archive, this method Updates the content of that given filename
        ///   in the zip archive.
        /// </para>
        ///
        /// <para>
        ///   This version of the method allows the caller to explicitly specify the
        ///   directory path to be used in the archive.  The entry to be added or
        ///   updated is found by using the specified directory path, combined with the
        ///   basename of the specified filename.
        /// </para>
        ///
        /// <para>
        ///   Upon success, there is no way for the application to learn if the file was
        ///   added versus updated.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string,string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string,string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
        ///
        /// <param name="fileName">
        ///   The name of the file to add or update. It should refer to a file in the
        ///   filesystem.  The name of the file may be a relative path or a
        ///   fully-qualified path.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the
        ///   <c>fileName</c>.  This path may, or may not, correspond to a real
        ///   directory in the current filesystem.  If the files within the zip are
        ///   later extracted, this is the path used for the extracted file.  Passing
        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
        ///   <c>fileName</c>, if any.  Passing the empty string ("") will insert the
        ///   item at the root path within the archive.
        /// </param>
        ///
        /// <returns>
        ///   The <c>ZipEntry</c> corresponding to the File that was added or updated.
        /// </returns>
        public ZipEntry UpdateFile(string fileName, String directoryPathInArchive)
        {
            // ideally this would all be transactional!
            var key = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
            if (this[key] != null)
                this.RemoveEntry(key);
            return this.AddFile(fileName, directoryPathInArchive);
        }





        /// <summary>
        ///   Add or update a directory in a zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///   If the specified directory does not exist in the archive, then this method
        ///   is equivalent to calling <c>AddDirectory()</c>.  If the specified
        ///   directory already exists in the archive, then this method updates any
        ///   existing entries, and adds any new entries. Any entries that are in the
        ///   zip archive but not in the specified directory, are left alone.  In other
        ///   words, the contents of the zip file will be a union of the previous
        ///   contents and the new files.
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string)"/>
        ///
        /// <param name="directoryName">
        ///   The path to the directory to be added to the zip archive, or updated in
        ///   the zip archive.
        /// </param>
        ///
        /// <returns>
        /// The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
        /// </returns>
        public ZipEntry UpdateDirectory(string directoryName)
        {
            return UpdateDirectory(directoryName, null);
        }


        /// <summary>
        ///   Add or update a directory in the zip archive at the specified root
        ///   directory in the archive.
        /// </summary>
        ///
        /// <remarks>
        ///   If the specified directory does not exist in the archive, then this method
        ///   is equivalent to calling <c>AddDirectory()</c>.  If the specified
        ///   directory already exists in the archive, then this method updates any
        ///   existing entries, and adds any new entries. Any entries that are in the
        ///   zip archive but not in the specified directory, are left alone.  In other
        ///   words, the contents of the zip file will be a union of the previous
        ///   contents and the new files.
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string,string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string,string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateItem(string,string)"/>
        ///
        /// <param name="directoryName">
        ///   The path to the directory to be added to the zip archive, or updated
        ///   in the zip archive.
        /// </param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the
        ///   <c>directoryName</c>.  This path may, or may not, correspond to a real
        ///   directory in the current filesystem.  If the files within the zip are
        ///   later extracted, this is the path used for the extracted file.  Passing
        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
        ///   <c>directoryName</c>, if any.  Passing the empty string ("") will insert
        ///   the item at the root path within the archive.
        /// </param>
        ///
        /// <returns>
        ///   The <c>ZipEntry</c> corresponding to the Directory that was added or updated.
        /// </returns>
        public ZipEntry UpdateDirectory(string directoryName, String directoryPathInArchive)
        {
            return this.AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOrUpdate);
        }





        /// <summary>
        ///   Add or update a file or directory in the zip archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This is useful when the application is not sure or does not care if the
        ///   item to be added is a file or directory, and does not know or does not
        ///   care if the item already exists in the <c>ZipFile</c>. Calling this method
        ///   is equivalent to calling <c>RemoveEntry()</c> if an entry by the same name
        ///   already exists, followed calling by <c>AddItem()</c>.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
        ///
        /// <param name="itemName">
        ///  the path to the file or directory to be added or updated.
        /// </param>
        public void UpdateItem(string itemName)
        {
            UpdateItem(itemName, null);
        }


        /// <summary>
        ///   Add or update a file or directory.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method is useful when the application is not sure or does not care if
        ///   the item to be added is a file or directory, and does not know or does not
        ///   care if the item already exists in the <c>ZipFile</c>. Calling this method
        ///   is equivalent to calling <c>RemoveEntry()</c>, if an entry by that name
        ///   exists, and then calling <c>AddItem()</c>.
        /// </para>
        ///
        /// <para>
        ///   This version of the method allows the caller to explicitly specify the
        ///   directory path to be used for the item being added to the archive.  The
        ///   entry or entries that are added or updated will use the specified
        ///   <c>DirectoryPathInArchive</c>. Extracting the entry from the archive will
        ///   result in a file stored in that directory path.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateFile(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
        ///
        /// <param name="itemName">
        ///   The path for the File or Directory to be added or updated.
        /// </param>
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the
        ///   <c>itemName</c>.  This path may, or may not, correspond to a real
        ///   directory in the current filesystem.  If the files within the zip are
        ///   later extracted, this is the path used for the extracted file.  Passing
        ///   <c>null</c> (<c>Nothing</c> in VB) will use the path on the
        ///   <c>itemName</c>, if any.  Passing the empty string ("") will insert the
        ///   item at the root path within the archive.
        /// </param>
        public void UpdateItem(string itemName, string directoryPathInArchive)
        {
            if (File.Exists(itemName))
                UpdateFile(itemName, directoryPathInArchive);

            else if (Directory.Exists(itemName))
                UpdateDirectory(itemName, directoryPathInArchive);

            else
                throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!", itemName));
        }




        /// <summary>
        ///   Adds a named entry into the zip archive, taking content for the entry
        ///   from a string.
        /// </summary>
        ///
        /// <remarks>
        ///   Calling this method creates an entry using the given fileName and
        ///   directory path within the archive.  There is no need for a file by the
        ///   given name to exist in the filesystem; the name is used within the zip
        ///   archive only. The content for the entry is encoded using the default text
        ///   encoding for the machine, or on Silverlight, using UTF-8.
        /// </remarks>
        ///
        /// <param name="content">
        ///   The content of the file, should it be extracted from the zip.
        /// </param>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use for the entry within the archive.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        /// <example>
        ///
        /// This example shows how to add an entry to the zipfile, using a string as
        /// content for that entry.
        ///
        /// <code lang="C#">
        /// string Content = "This string will be the content of the Readme.txt file in the zip archive.";
        /// using (ZipFile zip1 = new ZipFile())
        /// {
        ///   zip1.AddFile("MyDocuments\\Resume.doc", "files");
        ///   zip1.AddEntry("Readme.txt", Content);
        ///   zip1.Comment = "This zip file was created at " + System.DateTime.Now.ToString("G");
        ///   zip1.Save("Content.zip");
        /// }
        ///
        /// </code>
        /// <code lang="VB">
        /// Public Sub Run()
        ///   Dim Content As String = "This string will be the content of the Readme.txt file in the zip archive."
        ///   Using zip1 As ZipFile = New ZipFile
        ///     zip1.AddEntry("Readme.txt", Content)
        ///     zip1.AddFile("MyDocuments\Resume.doc", "files")
        ///     zip1.Comment = ("This zip file was created at " &amp; DateTime.Now.ToString("G"))
        ///     zip1.Save("Content.zip")
        ///   End Using
        /// End Sub
        /// </code>
        /// </example>
        public ZipEntry AddEntry(string entryName, string content)
        {
#if SILVERLIGHT
            return AddEntry(entryName, content, System.Text.Encoding.UTF8);
#else
            return AddEntry(entryName, content, System.Text.Encoding.Default);
#endif
        }



        /// <summary>
        ///   Adds a named entry into the zip archive, taking content for the entry
        ///   from a string, and using the specified text encoding.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   Calling this method creates an entry using the given fileName and
        ///   directory path within the archive.  There is no need for a file by the
        ///   given name to exist in the filesystem; the name is used within the zip
        ///   archive only.
        /// </para>
        ///
        /// <para>
        ///   The content for the entry, a string value, is encoded using the given
        ///   text encoding. A BOM (byte-order-mark) is emitted into the file, if the
        ///   Encoding parameter is set for that.
        /// </para>
        ///
        /// <para>
        ///   Most Encoding classes support a constructor that accepts a boolean,
        ///   indicating whether to emit a BOM or not. For example see <see
        ///   cref="System.Text.UTF8Encoding(bool)"/>.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="content">
        ///   The content of the file, should it be extracted from the zip.
        /// </param>
        ///
        /// <param name="encoding">
        ///   The text encoding to use when encoding the string. Be aware: This is
        ///   distinct from the text encoding used to encode the fileName, as specified
        ///   in <see cref="ProvisionalAlternateEncoding" />.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        public ZipEntry AddEntry(string entryName, string content, System.Text.Encoding encoding)
        {
            // cannot employ a using clause here.  We need the stream to
            // persist after exit from this method.
            var ms = new MemoryStream();

            // cannot use a using clause here; StreamWriter takes
            // ownership of the stream and Disposes it before we are ready.
            var sw = new StreamWriter(ms, encoding);
            sw.Write(content);
            sw.Flush();

            // reset to allow reading later
            ms.Seek(0, SeekOrigin.Begin);

            return AddEntry(entryName, ms);

            // must not dispose the MemoryStream - it will be used later.
        }


        /// <summary>
        ///   Create an entry in the <c>ZipFile</c> using the given <c>Stream</c>
        ///   as input.  The entry will have the given filename.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   The application should provide an open, readable stream; in this case it
        ///   will be read during the call to <see cref="ZipFile.Save()"/> or one of
        ///   its overloads.
        /// </para>
        ///
        /// <para>
        ///   The passed stream will be read from its current position. If
        ///   necessary, callers should set the position in the stream before
        ///   calling AddEntry(). This might be appropriate when using this method
        ///   with a MemoryStream, for example.
        /// </para>
        ///
        /// <para>
        ///   In cases where a large number of streams will be added to the
        ///   <c>ZipFile</c>, the application may wish to avoid maintaining all of the
        ///   streams open simultaneously.  To handle this situation, the application
        ///   should use the <see cref="AddEntry(string, OpenDelegate, CloseDelegate)"/>
        ///   overload.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <para>
        ///   This example adds a single entry to a <c>ZipFile</c> via a <c>Stream</c>.
        /// </para>
        ///
        /// <code lang="C#">
        /// String zipToCreate = "Content.zip";
        /// String fileNameInArchive = "Content-From-Stream.bin";
        /// using (System.IO.Stream streamToRead = MyStreamOpener())
        /// {
        ///   using (ZipFile zip = new ZipFile())
        ///   {
        ///     ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead);
        ///     zip.AddFile("Readme.txt");
        ///     zip.Save(zipToCreate);  // the stream is read implicitly here
        ///   }
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        /// Dim zipToCreate As String = "Content.zip"
        /// Dim fileNameInArchive As String = "Content-From-Stream.bin"
        /// Using streamToRead as System.IO.Stream = MyStreamOpener()
        ///   Using zip As ZipFile = New ZipFile()
        ///     Dim entry as ZipEntry = zip.AddEntry(fileNameInArchive, streamToRead)
        ///     zip.AddFile("Readme.txt")
        ///     zip.Save(zipToCreate)  '' the stream is read implicitly, here
        ///   End Using
        /// End Using
        /// </code>
        /// </example>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateEntry(string, System.IO.Stream)"/>
        ///
        /// <param name="entryName">
        ///   The name, including any path, which is shown in the zip file for the added
        ///   entry.
        /// </param>
        /// <param name="stream">
        ///   The input stream from which to grab content for the file
        /// </param>
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddEntry(string entryName, Stream stream)
        {
            ZipEntry ze = ZipEntry.CreateForStream(entryName, stream);
            ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            return _InternalAddEntry(ze);
        }



        /// <summary>
        ///   Add a ZipEntry for which content is written directly by the application.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   When the application needs to write the zip entry data, use this
        ///   method to add the ZipEntry. For example, in the case that the
        ///   application wishes to write the XML representation of a DataSet into
        ///   a ZipEntry, the application can use this method to do so.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// <para>
        ///   About progress events: When using the WriteDelegate, DotNetZip does
        ///   not issue any SaveProgress events with <c>EventType</c> = <see
        ///   cref="ZipProgressEventType.Saving_EntryBytesRead">
        ///   Saving_EntryBytesRead</see>. (This is because it is the
        ///   application's code that runs in WriteDelegate - there's no way for
        ///   DotNetZip to know when to issue a EntryBytesRead event.)
        ///   Applications that want to update a progress bar or similar status
        ///   indicator should do so from within the WriteDelegate
        ///   itself. DotNetZip will issue the other SaveProgress events,
        ///   including <see cref="ZipProgressEventType.Saving_Started">
        ///   Saving_Started</see>,
        ///   <see cref="ZipProgressEventType.Saving_BeforeWriteEntry">
        ///   Saving_BeforeWriteEntry</see>, and <see
        ///   cref="ZipProgressEventType.Saving_AfterWriteEntry">
        ///   Saving_AfterWriteEntry</see>.
        /// </para>
        ///
        /// <para>
        ///   Note: When you use PKZip encryption, it's normally necessary to
        ///   compute the CRC of the content to be encrypted, before compressing or
        ///   encrypting it. Therefore, when using PKZip encryption with a
        ///   WriteDelegate, the WriteDelegate CAN BE called twice: once to compute
        ///   the CRC, and the second time to potentially compress and
        ///   encrypt. Surprising, but true. This is because PKWARE specified that
        ///   the encryption initialization data depends on the CRC.
        ///   If this happens, for each call of the delegate, your
        ///   application must stream the same entry data in its entirety. If your
        ///   application writes different data during the second call, it will
        ///   result in a corrupt zip file.
        /// </para>
        ///
        /// <para>
        ///   The double-read behavior happens with all types of entries, not only
        ///   those that use WriteDelegate. It happens if you add an entry from a
        ///   filesystem file, or using a string, or a stream, or an opener/closer
        ///   pair. But in those cases, DotNetZip takes care of reading twice; in
        ///   the case of the WriteDelegate, the application code gets invoked
        ///   twice. Be aware.
        /// </para>
        ///
        /// <para>
        ///   As you can imagine, this can cause performance problems for large
        ///   streams, and it can lead to correctness problems when you use a
        ///   <c>WriteDelegate</c>. This is a pretty big pitfall.  There are two
        ///   ways to avoid it.  First, and most preferred: don't use PKZIP
        ///   encryption.  If you use the WinZip AES encryption, this problem
        ///   doesn't occur, because the encryption protocol doesn't require the CRC
        ///   up front. Second: if you do choose to use PKZIP encryption, write out
        ///   to a non-seekable stream (like standard output, or the
        ///   Response.OutputStream in an ASP.NET application).  In this case,
        ///   DotNetZip will use an alternative encryption protocol that does not
        ///   rely on the CRC of the content.  This also implies setting bit 3 in
        ///   the zip entry, which still presents problems for some zip tools.
        /// </para>
        ///
        /// <para>
        ///   In the future I may modify DotNetZip to *always* use bit 3 when PKZIP
        ///   encryption is in use.  This seems like a win overall, but there will
        ///   be some work involved.  If you feel strongly about it, visit the
        ///   DotNetZip forums and vote up <see
        ///   href="http://dotnetzip.codeplex.com/workitem/13686">the Workitem
        ///   tracking this issue</see>.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="entryName">the name of the entry to add</param>
        /// <param name="writer">the delegate which will write the entry content</param>
        /// <returns>the ZipEntry added</returns>
        ///
        /// <example>
        ///
        ///   This example shows an application filling a DataSet, then saving the
        ///   contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an
        ///   anonymous delegate in C#. The DataSet XML is never saved to a disk file.
        ///
        /// <code lang="C#">
        /// var c1= new System.Data.SqlClient.SqlConnection(connstring1);
        /// var da = new System.Data.SqlClient.SqlDataAdapter()
        ///     {
        ///         SelectCommand=  new System.Data.SqlClient.SqlCommand(strSelect, c1)
        ///     };
        ///
        /// DataSet ds1 = new DataSet();
        /// da.Fill(ds1, "Invoices");
        ///
        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        /// {
        ///     zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) );
        ///     zip.Save(zipFileName);
        /// }
        /// </code>
        /// </example>
        ///
        /// <example>
        ///
        /// This example uses an anonymous method in C# as the WriteDelegate to provide
        /// the data for the ZipEntry. The example is a bit contrived - the
        /// <c>AddFile()</c> method is a simpler way to insert the contents of a file
        /// into an entry in a zip file. On the other hand, if there is some sort of
        /// processing or transformation of the file contents required before writing,
        /// the application could use the <c>WriteDelegate</c> to do it, in this way.
        ///
        /// <code lang="C#">
        /// using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ))
        /// {
        ///     using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        ///     {
        ///         zip.AddEntry(zipEntryName, (name,output) =>
        ///             {
        ///                 byte[] buffer = new byte[BufferSize];
        ///                 int n;
        ///                 while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
        ///                 {
        ///                     // could transform the data here...
        ///                     output.Write(buffer, 0, n);
        ///                     // could update a progress bar here
        ///                 }
        ///             });
        ///
        ///         zip.Save(zipFileName);
        ///     }
        /// }
        /// </code>
        /// </example>
        ///
        /// <example>
        ///
        /// This example uses a named delegate in VB to write data for the given
        /// ZipEntry (VB9 does not have anonymous delegates). The example here is a bit
        /// contrived - a simpler way to add the contents of a file to a ZipEntry is to
        /// simply use the appropriate <c>AddFile()</c> method.  The key scenario for
        /// which the <c>WriteDelegate</c> makes sense is saving a DataSet, in XML
        /// format, to the zip file. The DataSet can write XML to a stream, and the
        /// WriteDelegate is the perfect place to write into the zip file.  There may be
        /// other data structures that can write to a stream, but cannot be read as a
        /// stream.  The <c>WriteDelegate</c> would be appropriate for those cases as
        /// well.
        ///
        /// <code lang="VB">
        /// Private Sub WriteEntry (ByVal name As String, ByVal output As Stream)
        ///     Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        ///         Dim n As Integer = -1
        ///         Dim buffer As Byte() = New Byte(BufferSize){}
        ///         Do While n &lt;&gt; 0
        ///             n = input.Read(buffer, 0, buffer.Length)
        ///             output.Write(buffer, 0, n)
        ///         Loop
        ///     End Using
        /// End Sub
        ///
        /// Public Sub Run()
        ///     Using zip = New ZipFile
        ///         zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
        ///         zip.Save(zipFileName)
        ///     End Using
        /// End Sub
        /// </code>
        /// </example>
        public ZipEntry AddEntry(string entryName, WriteDelegate writer)
        {
            ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer);
            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            return _InternalAddEntry(ze);
        }


        /// <summary>
        ///   Add an entry, for which the application will provide a stream
        ///   containing the entry data, on a just-in-time basis.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   In cases where the application wishes to open the stream that
        ///   holds the content for the ZipEntry, on a just-in-time basis, the
        ///   application can use this method.  The application provides an
        ///   opener delegate that will be called by the DotNetZip library to
        ///   obtain a readable stream that can be read to get the bytes for
        ///   the given entry.  Typically, this delegate opens a stream.
        ///   Optionally, the application can provide a closer delegate as
        ///   well, which will be called by DotNetZip when all bytes have been
        ///   read from the entry.
        /// </para>
        ///
        /// <para>
        ///   These delegates are called from within the scope of the call to
        ///   ZipFile.Save().
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        ///
        ///   This example uses anonymous methods in C# to open and close the
        ///   source stream for the content for a zip entry.
        ///
        /// <code lang="C#">
        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        /// {
        ///     zip.AddEntry(zipEntryName,
        ///                  (name) =>  File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ),
        ///                  (name, stream) =>  stream.Close()
        ///                  );
        ///
        ///     zip.Save(zipFileName);
        /// }
        /// </code>
        ///
        /// </example>
        ///
        /// <example>
        ///
        ///   This example uses delegates in VB.NET to open and close the
        ///   the source stream for the content for a zip entry.  VB 9.0 lacks
        ///   support for "Sub" lambda expressions, and so the CloseDelegate must
        ///   be an actual, named Sub.
        ///
        /// <code lang="VB">
        ///
        /// Function MyStreamOpener(ByVal entryName As String) As Stream
        ///     '' This simply opens a file.  You probably want to do somethinig
        ///     '' more involved here: open a stream to read from a database,
        ///     '' open a stream on an HTTP connection, and so on.
        ///     Return File.OpenRead(entryName)
        /// End Function
        ///
        /// Sub MyStreamCloser(entryName As String, stream As Stream)
        ///     stream.Close()
        /// End Sub
        ///
        /// Public Sub Run()
        ///     Dim dirToZip As String = "fodder"
        ///     Dim zipFileToCreate As String = "Archive.zip"
        ///     Dim opener As OpenDelegate = AddressOf MyStreamOpener
        ///     Dim closer As CloseDelegate = AddressOf MyStreamCloser
        ///     Dim numFilestoAdd As Int32 = 4
        ///     Using zip As ZipFile = New ZipFile
        ///         Dim i As Integer
        ///         For i = 0 To numFilesToAdd - 1
        ///             zip.AddEntry(String.Format("content-{0:000}.txt"), opener, closer)
        ///         Next i
        ///         zip.Save(zipFileToCreate)
        ///     End Using
        /// End Sub
        ///
        /// </code>
        /// </example>
        ///
        /// <param name="entryName">the name of the entry to add</param>
        /// <param name="opener">
        ///  the delegate that will be invoked by ZipFile.Save() to get the
        ///  readable stream for the given entry. ZipFile.Save() will call
        ///  read on this stream to obtain the data for the entry. This data
        ///  will then be compressed and written to the newly created zip
        ///  file.
        /// </param>
        /// <param name="closer">
        ///  the delegate that will be invoked to close the stream. This may
        ///  be null (Nothing in VB), in which case no call is makde to close
        ///  the stream.
        /// </param>
        /// <returns>the ZipEntry added</returns>
        ///
        public ZipEntry AddEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
        {
            ZipEntry ze = ZipEntry.CreateForJitStreamProvider(entryName, opener, closer);
            ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
            if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            return _InternalAddEntry(ze);
        }



        private ZipEntry _InternalAddEntry(ZipEntry ze)
        {
            // stamp all the props onto the entry
            ze._container = new ZipContainer(this);
            ze.CompressionMethod = this.CompressionMethod;
            ze.CompressionLevel = this.CompressionLevel;
            ze.ExtractExistingFile = this.ExtractExistingFile;
            ze.ZipErrorAction = this.ZipErrorAction;
            ze.SetCompression = this.SetCompression;
            ze.AlternateEncoding = this.AlternateEncoding;
            ze.AlternateEncodingUsage = this.AlternateEncodingUsage;
            ze.Password = this._Password;
            ze.Encryption = this.Encryption;
            ze.EmitTimesInWindowsFormatWhenSaving = this._emitNtfsTimes;
            ze.EmitTimesInUnixFormatWhenSaving = this._emitUnixTimes;
            //string key = DictionaryKeyForEntry(ze);
            InternalAddEntry(ze.FileName,ze);
            AfterAddEntry(ze);
            return ze;
        }




        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given
        ///   string as content for the <c>ZipEntry</c>.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for
        ///   the given file name and directory path, if it exists, and then calling
        ///   <see cref="AddEntry(String,String)" />.  See the documentation for
        ///   that method for further explanation. The string content is encoded
        ///   using the default encoding for the machine, or on Silverlight, using
        ///   UTF-8. This encoding is distinct from the encoding used for the
        ///   filename itself.  See <see cref="AlternateEncoding"/>.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="content">
        ///   The content of the file, should it be extracted from the zip.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        public ZipEntry UpdateEntry(string entryName, string content)
        {
#if SILVERLIGHT
            return UpdateEntry(entryName, content, System.Text.Encoding.UTF8);
#else
            return UpdateEntry(entryName, content, System.Text.Encoding.Default);
#endif
        }


        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given string as
        ///   content for the <c>ZipEntry</c>.
        /// </summary>
        ///
        /// <remarks>
        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
        ///   given file name and directory path, if it exists, and then calling <see
        ///   cref="AddEntry(String,String, System.Text.Encoding)" />.  See the
        ///   documentation for that method for further explanation.
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="content">
        ///   The content of the file, should it be extracted from the zip.
        /// </param>
        ///
        /// <param name="encoding">
        ///   The text encoding to use when encoding the string. Be aware: This is
        ///   distinct from the text encoding used to encode the filename. See <see
        ///   cref="AlternateEncoding" />.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        public ZipEntry UpdateEntry(string entryName, string content, System.Text.Encoding encoding)
        {
            RemoveEntryForUpdate(entryName);
            return AddEntry(entryName, content, encoding);
        }



        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given delegate
        ///   as the source for content for the <c>ZipEntry</c>.
        /// </summary>
        ///
        /// <remarks>
        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
        ///   given file name and directory path, if it exists, and then calling <see
        ///   cref="AddEntry(String,WriteDelegate)" />.  See the
        ///   documentation for that method for further explanation.
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="writer">the delegate which will write the entry content.</param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        public ZipEntry UpdateEntry(string entryName, WriteDelegate writer)
        {
            RemoveEntryForUpdate(entryName);
            return AddEntry(entryName, writer);
        }



        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given delegates
        ///   to open and close the stream that provides the content for the <c>ZipEntry</c>.
        /// </summary>
        ///
        /// <remarks>
        ///   Calling this method is equivalent to removing the <c>ZipEntry</c> for the
        ///   given file name and directory path, if it exists, and then calling <see
        ///   cref="AddEntry(String,OpenDelegate, CloseDelegate)" />.  See the
        ///   documentation for that method for further explanation.
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="opener">
        ///  the delegate that will be invoked to open the stream
        /// </param>
        /// <param name="closer">
        ///  the delegate that will be invoked to close the stream
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added or updated.</returns>
        ///
        public ZipEntry UpdateEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
        {
            RemoveEntryForUpdate(entryName);
            return AddEntry(entryName, opener, closer);
        }


        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given stream as
        ///   input, and the given filename and given directory Path.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Calling the method is equivalent to calling <c>RemoveEntry()</c> if an
        ///   entry by the same name already exists, and then calling <c>AddEntry()</c>
        ///   with the given <c>fileName</c> and stream.
        /// </para>
        ///
        /// <para>
        ///   The stream must be open and readable during the call to
        ///   <c>ZipFile.Save</c>.  You can dispense the stream on a just-in-time basis
        ///   using the <see cref="ZipEntry.InputStream"/> property. Check the
        ///   documentation of that property for more information.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, System.IO.Stream)"/>
        /// <seealso cref="ZipEntry.InputStream"/>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="stream">The input stream from which to read file data.</param>
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry UpdateEntry(string entryName, Stream stream)
        {
            RemoveEntryForUpdate(entryName);
            return AddEntry(entryName, stream);
        }


        private void RemoveEntryForUpdate(string entryName)
        {
            if (String.IsNullOrEmpty(entryName))
                throw new ArgumentNullException("entryName");

            string directoryPathInArchive = null;
            if (entryName.IndexOf('\\') != -1)
            {
                directoryPathInArchive = Path.GetDirectoryName(entryName);
                entryName = Path.GetFileName(entryName);
            }
            var key = ZipEntry.NameInArchive(entryName, directoryPathInArchive);
            if (this[key] != null)
                this.RemoveEntry(key);
        }




        /// <summary>
        ///   Add an entry into the zip archive using the given filename and
        ///   directory path within the archive, and the given content for the
        ///   file. No file is created in the filesystem.
        /// </summary>
        ///
        /// <param name="byteContent">The data to use for the entry.</param>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddEntry(string entryName, byte[] byteContent)
        {
            if (byteContent == null) throw new ArgumentException("bad argument", "byteContent");
            var ms = new MemoryStream(byteContent);
            return AddEntry(entryName, ms);
        }


        /// <summary>
        ///   Updates the given entry in the <c>ZipFile</c>, using the given byte
        ///   array as content for the entry.
        /// </summary>
        ///
        /// <remarks>
        ///   Calling this method is equivalent to removing the <c>ZipEntry</c>
        ///   for the given filename and directory path, if it exists, and then
        ///   calling <see cref="AddEntry(String,byte[])" />.  See the
        ///   documentation for that method for further explanation.
        /// </remarks>
        ///
        /// <param name="entryName">
        ///   The name, including any path, to use within the archive for the entry.
        /// </param>
        ///
        /// <param name="byteContent">The content to use for the <c>ZipEntry</c>.</param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        ///
        public ZipEntry UpdateEntry(string entryName, byte[] byteContent)
        {
            RemoveEntryForUpdate(entryName);
            return AddEntry(entryName, byteContent);
        }


//         private string DictionaryKeyForEntry(ZipEntry ze1)
//         {
//             var filename = SharedUtilities.NormalizePathForUseInZipFile(ze1.FileName);
//             return filename;
//         }


        /// <summary>
        ///   Adds the contents of a filesystem directory to a Zip file archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   The name of the directory may be a relative path or a fully-qualified
        ///   path. Any files within the named directory are added to the archive.  Any
        ///   subdirectories within the named directory are also added to the archive,
        ///   recursively.
        /// </para>
        ///
        /// <para>
        ///   Top-level entries in the named directory will appear as top-level entries
        ///   in the zip archive.  Entries in subdirectories in the named directory will
        ///   result in entries in subdirectories in the zip archive.
        /// </para>
        ///
        /// <para>
        ///   If you want the entries to appear in a containing directory in the zip
        ///   archive itself, then you should call the AddDirectory() overload that
        ///   allows you to explicitly specify a directory path for use in the archive.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddDirectory(string, string)"/>
        ///
        /// <overloads>This method has 2 overloads.</overloads>
        ///
        /// <param name="directoryName">The name of the directory to add.</param>
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddDirectory(string directoryName)
        {
            return AddDirectory(directoryName, null);
        }


        /// <summary>
        ///   Adds the contents of a filesystem directory to a Zip file archive,
        ///   overriding the path to be used for entries in the archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The name of the directory may be a relative path or a fully-qualified
        ///   path. The add operation is recursive, so that any files or subdirectories
        ///   within the name directory are also added to the archive.
        /// </para>
        ///
        /// <para>
        ///   Top-level entries in the named directory will appear as top-level entries
        ///   in the zip archive.  Entries in subdirectories in the named directory will
        ///   result in entries in subdirectories in the zip archive.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to each
        ///   ZipEntry added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <para>
        ///   In this code, calling the ZipUp() method with a value of "c:\reports" for
        ///   the directory parameter will result in a zip file structure in which all
        ///   entries are contained in a toplevel "reports" directory.
        /// </para>
        ///
        /// <code lang="C#">
        /// public void ZipUp(string targetZip, string directory)
        /// {
        ///   using (var zip = new ZipFile())
        ///   {
        ///     zip.AddDirectory(directory, System.IO.Path.GetFileName(directory));
        ///     zip.Save(targetZip);
        ///   }
        /// }
        /// </code>
        /// </example>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddItem(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.AddFile(string, string)"/>
        /// <seealso cref="Ionic.Zip.ZipFile.UpdateDirectory(string, string)"/>
        ///
        /// <param name="directoryName">The name of the directory to add.</param>
        ///
        /// <param name="directoryPathInArchive">
        ///   Specifies a directory path to use to override any path in the
        ///   DirectoryName.  This path may, or may not, correspond to a real directory
        ///   in the current filesystem.  If the zip is later extracted, this is the
        ///   path used for the extracted file or directory.  Passing <c>null</c>
        ///   (<c>Nothing</c> in VB) or the empty string ("") will insert the items at
        ///   the root path within the archive.
        /// </param>
        ///
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddDirectory(string directoryName, string directoryPathInArchive)
        {
            return AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOnly);
        }


        /// <summary>
        ///   Creates a directory in the zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   Use this when you want to create a directory in the archive but there is
        ///   no corresponding filesystem representation for that directory.
        /// </para>
        ///
        /// <para>
        ///   You will probably not need to do this in your code. One of the only times
        ///   you will want to do this is if you want an empty directory in the zip
        ///   archive.  The reason: if you add a file to a zip archive that is stored
        ///   within a multi-level directory, all of the directory tree is implicitly
        ///   created in the zip archive.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="directoryNameInArchive">
        ///   The name of the directory to create in the archive.
        /// </param>
        /// <returns>The <c>ZipEntry</c> added.</returns>
        public ZipEntry AddDirectoryByName(string directoryNameInArchive)
        {
            // workitem 9073
            ZipEntry dir = ZipEntry.CreateFromNothing(directoryNameInArchive);
            dir._container = new ZipContainer(this);
            dir.MarkAsDirectory();
            dir.AlternateEncoding = this.AlternateEncoding;  // workitem 8984
            dir.AlternateEncodingUsage = this.AlternateEncodingUsage;
            dir.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now);
            dir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
            dir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;
            dir._Source = ZipEntrySource.Stream;
            //string key = DictionaryKeyForEntry(dir);
            InternalAddEntry(dir.FileName,dir);
            AfterAddEntry(dir);
            return dir;
        }



        private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
                                                  string rootDirectoryPathInArchive,
                                                  AddOrUpdateAction action)
        {
            if (rootDirectoryPathInArchive == null)
            {
                rootDirectoryPathInArchive = "";
            }

            return AddOrUpdateDirectoryImpl(directoryName, rootDirectoryPathInArchive, action, true, 0);
        }


        internal void InternalAddEntry(String name, ZipEntry entry)
        {
            _entries.Add(name, entry);
            _zipEntriesAsList = null;
            _contentsChanged = true;
        }



        private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
                                                  string rootDirectoryPathInArchive,
                                                  AddOrUpdateAction action,
                                                  bool recurse,
                                                  int level)
        {
            if (Verbose)
                StatusMessageTextWriter.WriteLine("{0} {1}...",
                                                  (action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating",
                                                  directoryName);

            if (level == 0)
            {
                _addOperationCanceled = false;
                OnAddStarted();
            }

            // workitem 13371
            if (_addOperationCanceled)
                return null;

            string dirForEntries = rootDirectoryPathInArchive;
            ZipEntry baseDir = null;

            if (level > 0)
            {
                int f = directoryName.Length;
                for (int i = level; i > 0; i--)
                    f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1);

                dirForEntries = directoryName.Substring(f + 1);
                dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries);
            }

            // if not top level, or if the root is non-empty, then explicitly add the directory
            if (level > 0 || rootDirectoryPathInArchive != "")
            {
                baseDir = ZipEntry.CreateFromFile(directoryName, dirForEntries);
                baseDir._container = new ZipContainer(this);
                baseDir.AlternateEncoding = this.AlternateEncoding;  // workitem 6410
                baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage;
                baseDir.MarkAsDirectory();
                baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
                baseDir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes;

                // add the directory only if it does not exist.
                // It's not an error if it already exists.
                if (!_entries.ContainsKey(baseDir.FileName))
                {
                    InternalAddEntry(baseDir.FileName,baseDir);
                    AfterAddEntry(baseDir);
                }
                dirForEntries = baseDir.FileName;
            }

            if (!_addOperationCanceled)
            {

                String[] filenames = Directory.GetFiles(directoryName);

                if (recurse)
                {
                    // add the files:
                    foreach (String filename in filenames)
                    {
                        if (_addOperationCanceled) break;
                        if (action == AddOrUpdateAction.AddOnly)
                            AddFile(filename, dirForEntries);
                        else
                            UpdateFile(filename, dirForEntries);
                    }

                    if (!_addOperationCanceled)
                    {
                        // add the subdirectories:
                        String[] dirnames = Directory.GetDirectories(directoryName);
                        foreach (String dir in dirnames)
                        {
                            // workitem 8617: Optionally traverse reparse points
#if SILVERLIGHT
#elif NETCF
                            FileAttributes fileAttrs = (FileAttributes) NetCfFile.GetAttributes(dir);
#else
                            FileAttributes fileAttrs = System.IO.File.GetAttributes(dir);
#endif
                            if (this.AddDirectoryWillTraverseReparsePoints
#if !SILVERLIGHT
                                || ((fileAttrs & FileAttributes.ReparsePoint) == 0)
#endif
                                )
                                AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1);

                        }

                    }
                }
            }

            if (level == 0)
                OnAddCompleted();

            return baseDir;
        }

    }

}
