* support-test-*.cs: Rename from test-*-p2.cs.
[mono.git] / mcs / class / ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / Zip / FastZip.cs
1 // SimpleZip.cs\r
2 //\r
3 // Copyright 2005 John Reilly\r
4 //\r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software\r
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
18 //\r
19 // Linking this library statically or dynamically with other modules is\r
20 // making a combined work based on this library.  Thus, the terms and\r
21 // conditions of the GNU General Public License cover the whole\r
22 // combination.\r
23 // \r
24 // As a special exception, the copyright holders of this library give you\r
25 // permission to link this library with independent modules to produce an\r
26 // executable, regardless of the license terms of these independent\r
27 // modules, and to copy and distribute the resulting executable under\r
28 // terms of your choice, provided that you also meet, for each linked\r
29 // independent module, the terms and conditions of the license of that\r
30 // module.  An independent module is a module which is not derived from\r
31 // or based on this library.  If you modify this library, you may extend\r
32 // this exception to your version of the library, but you are not\r
33 // obligated to do so.  If you do not wish to do so, delete this\r
34 // exception statement from your version.\r
35 \r
36 \r
37 using System;\r
38 using System.IO;\r
39 using ICSharpCode.SharpZipLib.Core;\r
40 \r
41 namespace ICSharpCode.SharpZipLib.Zip\r
42 {\r
43         /// <summary>\r
44         /// FastZipEvents supports all events applicable to <see cref="FastZip">FastZip</see> operations.\r
45         /// </summary>\r
46         public class FastZipEvents\r
47         {\r
48                 /// <summary>\r
49                 /// Delegate to invoke when processing directories.\r
50                 /// </summary>\r
51                 public ProcessDirectoryDelegate ProcessDirectory;\r
52                 \r
53                 /// <summary>\r
54                 /// Delegate to invoke when processing files.\r
55                 /// </summary>\r
56                 public ProcessFileDelegate ProcessFile;\r
57 \r
58                 /// <summary>\r
59                 /// Delegate to invoke when processing directory failures.\r
60                 /// </summary>\r
61                 public DirectoryFailureDelegate DirectoryFailure;\r
62                 \r
63                 /// <summary>\r
64                 /// Delegate to invoke when processing file failures.\r
65                 /// </summary>\r
66                 public FileFailureDelegate FileFailure;\r
67                 \r
68                 /// <summary>\r
69                 /// Raise the directory failure event.\r
70                 /// </summary>\r
71                 /// <param name="directory">The directory.</param>\r
72                 /// <param name="e">The exception for this event.</param>\r
73                 public void OnDirectoryFailure(string directory, Exception e)\r
74                 {\r
75                         if ( DirectoryFailure != null ) {\r
76                                 ScanFailureEventArgs args = new ScanFailureEventArgs(directory, e);\r
77                                 DirectoryFailure(this, args);\r
78                         }\r
79                 }\r
80                 \r
81                 /// <summary>\r
82                 /// Raises the file failure event.\r
83                 /// </summary>\r
84                 /// <param name="file">The file for this event.</param>\r
85                 /// <param name="e">The exception for this event.</param>\r
86                 public void OnFileFailure(string file, Exception e)\r
87                 {\r
88                         if ( FileFailure != null ) {\r
89                                 ScanFailureEventArgs args = new ScanFailureEventArgs(file, e);\r
90                                 FileFailure(this, args);\r
91                         }\r
92                 }\r
93                 \r
94                 /// <summary>\r
95                 /// Raises the ProcessFileEvent.\r
96                 /// </summary>\r
97                 /// <param name="file">The file for this event.</param>\r
98                 public void OnProcessFile(string file)\r
99                 {\r
100                         if ( ProcessFile != null ) {\r
101                                 ScanEventArgs args = new ScanEventArgs(file);\r
102                                 ProcessFile(this, args);\r
103                         }\r
104                 }\r
105                 \r
106                 /// <summary>\r
107                 /// Raises the ProcessDirectoryEvent.\r
108                 /// </summary>\r
109                 /// <param name="directory">The directory for this event.</param>\r
110                 /// <param name="hasMatchingFiles">Flag indicating if directory has matching files as determined by the current filter.</param>\r
111                 public void OnProcessDirectory(string directory, bool hasMatchingFiles)\r
112                 {\r
113                         if ( ProcessDirectory != null ) {\r
114                                 DirectoryEventArgs args = new DirectoryEventArgs(directory, hasMatchingFiles);\r
115                                 ProcessDirectory(this, args);\r
116                         }\r
117                 }\r
118                 \r
119         }\r
120         \r
121         /// <summary>\r
122         /// FastZip provides facilities for creating and extracting zip files.\r
123         /// Only relative paths are supported.\r
124         /// </summary>\r
125         public class FastZip\r
126         {\r
127                 /// <summary>\r
128                 /// Initialize a default instance of FastZip.\r
129                 /// </summary>\r
130                 public FastZip()\r
131                 {\r
132                         this.events = null;\r
133                 }\r
134                 \r
135                 /// <summary>\r
136                 /// Initialise a new instance of <see cref="FastZip"/>\r
137                 /// </summary>\r
138                 /// <param name="events"></param>\r
139                 public FastZip(FastZipEvents events)\r
140                 {\r
141                         this.events = events;\r
142                 }\r
143                 \r
144                 /// <summary>\r
145                 /// Defines the desired handling when overwriting files.\r
146                 /// </summary>\r
147                 public enum Overwrite {\r
148                         /// <summary>\r
149                         /// Prompt the user to confirm overwriting\r
150                         /// </summary>\r
151                         Prompt,\r
152                         /// <summary>\r
153                         /// Never overwrite files.\r
154                         /// </summary>\r
155                         Never,\r
156                         /// <summary>\r
157                         /// Always overwrite files.\r
158                         /// </summary>\r
159                         Always\r
160                 }\r
161 \r
162                 /// <summary>\r
163                 /// Get/set a value indicating wether empty directories should be created.\r
164                 /// </summary>\r
165                 public bool CreateEmptyDirectories\r
166                 {\r
167                         get { return createEmptyDirectories; }\r
168                         set { createEmptyDirectories = value; }\r
169                 }\r
170 \r
171                 /// <summary>\r
172                 /// Delegate called when confirming overwriting of files.\r
173                 /// </summary>\r
174                 public delegate bool ConfirmOverwriteDelegate(string fileName);\r
175                 \r
176                 /// <summary>\r
177                 /// Create a zip file.\r
178                 /// </summary>\r
179                 /// <param name="zipFileName">The name of the zip file to create.</param>\r
180                 /// <param name="sourceDirectory">The directory to source files from.</param>\r
181                 /// <param name="recurse">True to recurse directories, false for no recursion.</param>\r
182                 /// <param name="fileFilter">The file filter to apply.</param>\r
183                 /// <param name="directoryFilter">The directory filter to apply.</param>\r
184                 public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter)\r
185                 {\r
186                         NameTransform = new ZipNameTransform(true, sourceDirectory);\r
187                         this.sourceDirectory = sourceDirectory;\r
188                         \r
189                         outputStream = new ZipOutputStream(File.Create(zipFileName));\r
190                         try {\r
191                                 FileSystemScanner scanner = new FileSystemScanner(fileFilter, directoryFilter);\r
192                                 scanner.ProcessFile += new ProcessFileDelegate(ProcessFile);\r
193                                 if ( this.CreateEmptyDirectories ) {\r
194                                         scanner.ProcessDirectory += new ProcessDirectoryDelegate(ProcessDirectory);\r
195                                 }\r
196                                 scanner.Scan(sourceDirectory, recurse);\r
197                         }\r
198                         finally {\r
199                                 outputStream.Close();\r
200                         }\r
201                 }\r
202 \r
203                 /// <summary>\r
204                 /// Create a zip file/archive.\r
205                 /// </summary>\r
206                 /// <param name="zipFileName">The name of the zip file to create.</param>\r
207                 /// <param name="sourceDirectory">The directory to obtain files and directories from.</param>\r
208                 /// <param name="recurse">True to recurse directories, false for no recursion.</param>\r
209                 /// <param name="fileFilter">The file filter to apply.</param>\r
210                 public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter)\r
211                 {\r
212                         CreateZip(zipFileName, sourceDirectory, recurse, fileFilter, null);\r
213                 }\r
214 \r
215                 /// <summary>\r
216                 /// Extract the contents of a zip file.\r
217                 /// </summary>\r
218                 /// <param name="zipFileName">The zip file to extract from.</param>\r
219                 /// <param name="targetDirectory">The directory to save extracted information in.</param>\r
220                 /// <param name="fileFilter">A filter to apply to files.</param>\r
221                 public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter) \r
222                 {\r
223                         ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null);\r
224                 }\r
225                 \r
226                 /// <summary>\r
227                 /// Exatract the contents of a zip file.\r
228                 /// </summary>\r
229                 /// <param name="zipFileName">The zip file to extract from.</param>\r
230                 /// <param name="targetDirectory">The directory to save extracted information in.</param>\r
231                 /// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>\r
232                 /// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>\r
233                 /// <param name="fileFilter">A filter to apply to files.</param>\r
234                 /// <param name="directoryFilter">A filter to apply to directories.</param>\r
235                 public void ExtractZip(string zipFileName, string targetDirectory, \r
236                                        Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate, \r
237                                        string fileFilter, string directoryFilter)\r
238                 {\r
239                         if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) {\r
240                                 throw new ArgumentNullException("confirmDelegate");\r
241                         }\r
242                         this.overwrite = overwrite;\r
243                         this.confirmDelegate = confirmDelegate;\r
244                         this.targetDirectory = targetDirectory;\r
245                         this.fileFilter = new NameFilter(fileFilter);\r
246                         this.directoryFilter = new NameFilter(directoryFilter);\r
247                         \r
248                         inputStream = new ZipInputStream(File.OpenRead(zipFileName));\r
249                         \r
250                         try {\r
251                                 \r
252                                 if (password != null) {\r
253                                         inputStream.Password = password;\r
254                                 }\r
255 \r
256                                 ZipEntry entry;\r
257                                 while ( (entry = inputStream.GetNextEntry()) != null ) {\r
258                                         if ( this.directoryFilter.IsMatch(Path.GetDirectoryName(entry.Name)) && this.fileFilter.IsMatch(entry.Name) ) {\r
259                                                 ExtractEntry(entry);\r
260                                         }\r
261                                 }\r
262                         }\r
263                         finally {\r
264                                 inputStream.Close();\r
265                         }\r
266                 }\r
267                 \r
268                 void ProcessDirectory(object sender, DirectoryEventArgs e)\r
269                 {\r
270                         if ( !e.HasMatchingFiles && createEmptyDirectories ) {\r
271                                 if ( events != null ) {\r
272                                         events.OnProcessDirectory(e.Name, e.HasMatchingFiles);\r
273                                 }\r
274                                 \r
275                                 if (e.Name != sourceDirectory) {\r
276                                         string cleanedName = nameTransform.TransformDirectory(e.Name);\r
277                                         ZipEntry entry = new ZipEntry(cleanedName);\r
278                                         outputStream.PutNextEntry(entry);\r
279                                 }\r
280                         }\r
281                 }\r
282                 \r
283                 void ProcessFile(object sender, ScanEventArgs e)\r
284                 {\r
285                         if ( events != null ) {\r
286                                 events.OnProcessFile(e.Name);\r
287                         }\r
288                         string cleanedName = nameTransform.TransformFile(e.Name);\r
289                         ZipEntry entry = new ZipEntry(cleanedName);\r
290                         outputStream.PutNextEntry(entry);\r
291                         AddFileContents(e.Name);\r
292                 }\r
293 \r
294                 void AddFileContents(string name)\r
295                 {\r
296                         if ( buffer == null ) {\r
297                                 buffer = new byte[4096];\r
298                         }\r
299 \r
300                         FileStream stream = File.OpenRead(name);\r
301                         try {\r
302                                 int length;\r
303                                 do {\r
304                                         length = stream.Read(buffer, 0, buffer.Length);\r
305                                         outputStream.Write(buffer, 0, length);\r
306                                 } while ( length > 0 );\r
307                         }\r
308                         finally {\r
309                                 stream.Close();\r
310                         }\r
311                 }\r
312                 \r
313                 void ExtractFileEntry(ZipEntry entry, string targetName)\r
314                 {\r
315                         bool proceed = true;\r
316                         if ((overwrite == Overwrite.Prompt) && (confirmDelegate != null)) {\r
317                                 if (File.Exists(targetName) == true) {\r
318                                         proceed = confirmDelegate(targetName);\r
319                                 }\r
320                         }\r
321 \r
322                         if ( proceed ) {\r
323                                 \r
324                                 if ( events != null ) {\r
325                                         events.OnProcessFile(entry.Name);\r
326                                 }\r
327                         \r
328                                 FileStream streamWriter = File.Create(targetName);\r
329                         \r
330                                 try {\r
331                                         if ( buffer == null ) {\r
332                                                 buffer = new byte[4096];\r
333                                         }\r
334                                         \r
335                                         int size;\r
336                 \r
337                                         do {\r
338                                                 size = inputStream.Read(buffer, 0, buffer.Length);\r
339                                                 streamWriter.Write(buffer, 0, size);\r
340                                         } while (size > 0);\r
341                                 }\r
342                                 finally {\r
343                                         streamWriter.Close();\r
344                                 }\r
345         \r
346                                 if (restoreDateTime) {\r
347                                         File.SetLastWriteTime(targetName, entry.DateTime);\r
348                                 }\r
349                         }\r
350                 }\r
351 \r
352                 bool NameIsValid(string name)\r
353                 {\r
354                         return name != null && name.Length > 0 && name.IndexOfAny(Path.InvalidPathChars) < 0;\r
355                 }\r
356                 \r
357                 void ExtractEntry(ZipEntry entry)\r
358                 {\r
359                         bool doExtraction = NameIsValid(entry.Name);\r
360                         \r
361                         string dirName = null;\r
362                         string targetName = null;\r
363                         \r
364                         if ( doExtraction ) {\r
365                                 string entryFileName;\r
366                                 if (Path.IsPathRooted(entry.Name)) {\r
367                                         string workName = Path.GetPathRoot(entry.Name);\r
368                                         workName = entry.Name.Substring(workName.Length);\r
369                                         entryFileName = Path.Combine(Path.GetDirectoryName(workName), Path.GetFileName(entry.Name));\r
370                                 } else {\r
371                                         entryFileName = entry.Name;\r
372                                 }\r
373                                 \r
374                                 targetName = Path.Combine(targetDirectory, entryFileName);\r
375                                 dirName = Path.GetDirectoryName(Path.GetFullPath(targetName));\r
376         \r
377                                 doExtraction = doExtraction && (entryFileName.Length > 0);\r
378                         }\r
379                         \r
380                         if ( doExtraction && !Directory.Exists(dirName) )\r
381                         {\r
382                                 if ( !entry.IsDirectory || this.CreateEmptyDirectories ) {\r
383                                         try {\r
384                                                 Directory.CreateDirectory(dirName);\r
385                                         }\r
386                                         catch {\r
387                                                 doExtraction = false;\r
388                                         }\r
389                                 }\r
390                         }\r
391                         \r
392                         if ( doExtraction && entry.IsFile ) {\r
393                                 ExtractFileEntry(entry, targetName);\r
394                         }\r
395                 }\r
396                 \r
397                 /// <summary>\r
398                 /// Get or set the <see cref="ZipNameTransform"> active when creating Zip files.</see>\r
399                 /// </summary>\r
400                 public ZipNameTransform NameTransform\r
401                 {\r
402                         get { return nameTransform; }\r
403                         set {\r
404                                 if ( value == null ) {\r
405                                         nameTransform = new ZipNameTransform();\r
406                                 }\r
407                                 else {\r
408                                         nameTransform = value;\r
409                                 }\r
410                         }\r
411                 }\r
412                 \r
413                 #region Instance Fields\r
414                 byte[] buffer;\r
415                 ZipOutputStream outputStream;\r
416                 ZipInputStream inputStream;\r
417                 string password = null;\r
418                 string targetDirectory;\r
419                 string sourceDirectory;\r
420                 NameFilter fileFilter;\r
421                 NameFilter directoryFilter;\r
422                 Overwrite overwrite;\r
423                 ConfirmOverwriteDelegate confirmDelegate;\r
424                 bool restoreDateTime = false;\r
425                 bool createEmptyDirectories = false;\r
426                 FastZipEvents events;\r
427                 ZipNameTransform nameTransform;\r
428                 #endregion\r
429         }\r
430 }\r