New test.
[mono.git] / mcs / class / Compat.ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / Tar / TarEntry.cs
1 // TarEntry.cs\r
2 // Copyright (C) 2001 Mike Krueger\r
3 //\r
4 // This program is free software; you can redistribute it and/or\r
5 // modify it under the terms of the GNU General Public License\r
6 // as published by the Free Software Foundation; either version 2\r
7 // of the License, or (at your option) any later version.\r
8 //\r
9 // This program is distributed in the hope that it will be useful,\r
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
12 // GNU General Public License for more details.\r
13 //\r
14 // You should have received a copy of the GNU General Public License\r
15 // along with this program; if not, write to the Free Software\r
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
17 //\r
18 // Linking this library statically or dynamically with other modules is\r
19 // making a combined work based on this library.  Thus, the terms and\r
20 // conditions of the GNU General Public License cover the whole\r
21 // combination.\r
22 // \r
23 // As a special exception, the copyright holders of this library give you\r
24 // permission to link this library with independent modules to produce an\r
25 // executable, regardless of the license terms of these independent\r
26 // modules, and to copy and distribute the resulting executable under\r
27 // terms of your choice, provided that you also meet, for each linked\r
28 // independent module, the terms and conditions of the license of that\r
29 // module.  An independent module is a module which is not derived from\r
30 // or based on this library.  If you modify this library, you may extend\r
31 // this exception to your version of the library, but you are not\r
32 // obligated to do so.  If you do not wish to do so, delete this\r
33 // exception statement from your version.\r
34 \r
35 using System;\r
36 using System.IO;\r
37 using System.Text;\r
38 \r
39 namespace ICSharpCode.SharpZipLib.Tar \r
40 {\r
41         \r
42         /// <summary>\r
43         /// This class represents an entry in a Tar archive. It consists\r
44         /// of the entry's header, as well as the entry's File. Entries\r
45         /// can be instantiated in one of three ways, depending on how\r
46         /// they are to be used.\r
47         /// <p>\r
48         /// TarEntries that are created from the header bytes read from\r
49         /// an archive are instantiated with the TarEntry( byte[] )\r
50         /// constructor. These entries will be used when extracting from\r
51         /// or listing the contents of an archive. These entries have their\r
52         /// header filled in using the header bytes. They also set the File\r
53         /// to null, since they reference an archive entry not a file.</p>\r
54         /// <p>\r
55         /// TarEntries that are created from Files that are to be written\r
56         /// into an archive are instantiated with the TarEntry( File )\r
57         /// constructor. These entries have their header filled in using\r
58         /// the File's information. They also keep a reference to the File\r
59         /// for convenience when writing entries.</p>\r
60         /// <p>\r
61         /// Finally, TarEntries can be constructed from nothing but a name.\r
62         /// This allows the programmer to construct the entry by hand, for\r
63         /// instance when only an InputStream is available for writing to\r
64         /// the archive, and the header information is constructed from\r
65         /// other information. In this case the header fields are set to\r
66         /// defaults and the File is set to null.</p>\r
67         /// \r
68         /// <see cref="TarHeader"/>\r
69         /// </summary>\r
70         public class TarEntry\r
71         {\r
72                 /// <summary>\r
73                 /// If this entry represents a File, this references it.\r
74                 /// </summary>\r
75                 string    file;\r
76                 \r
77                 /// <summary>\r
78                 /// This is the entry's header information.\r
79                 /// </summary>\r
80                 TarHeader       header;\r
81                 \r
82                 /// <summary>\r
83                 /// Only Create Entries with the static CreateXYZ methods or a headerBuffer.\r
84                 /// </summary>\r
85                 private TarEntry()\r
86                 {\r
87                 }\r
88                 \r
89                 /// <summary>\r
90                 /// Construct an entry from an archive's header bytes. File is set\r
91                 /// to null.\r
92                 /// </summary>\r
93                 /// <param name = "headerBuf">\r
94                 /// The header bytes from a tar archive entry.\r
95                 /// </param>\r
96                 public TarEntry(byte[] headerBuf)\r
97                 {\r
98                         this.Initialize();\r
99                         this.header.ParseBuffer(headerBuf);\r
100                 }\r
101                 \r
102 \r
103       public TarEntry(TarHeader header)\r
104       {\r
105          file = null;\r
106          this.header = header;\r
107       }\r
108                 \r
109                 /// <summary>\r
110                 /// Construct an entry with only a name. This allows the programmer\r
111                 /// to construct the entry's header "by hand". File is set to null.\r
112                 /// </summary>\r
113                 public static TarEntry CreateTarEntry(string name)\r
114                 {\r
115                         TarEntry entry = new TarEntry();\r
116                         entry.Initialize();\r
117                         entry.NameTarHeader(entry.header, name);\r
118                         return entry;\r
119                 }\r
120                 \r
121                 /// <summary>\r
122                 /// Construct an entry for a file. File is set to file, and the\r
123                 /// header is constructed from information from the file.\r
124                 /// </summary>\r
125                 /// <param name = "fileName">\r
126                 /// The file that the entry represents.\r
127                 /// </param>\r
128                 public static TarEntry CreateEntryFromFile(string fileName)\r
129                 {\r
130                         TarEntry entry = new TarEntry();\r
131                         entry.Initialize();\r
132                         entry.GetFileTarHeader(entry.header, fileName);\r
133                         return entry;\r
134                 }\r
135                 \r
136                 /// <summary>\r
137                 /// Initialization code common to all pseudo constructors.\r
138                 /// </summary>\r
139                 void Initialize()\r
140                 {\r
141                         this.file   = null;\r
142                         this.header = new TarHeader();\r
143                 }\r
144                 \r
145                 /// <summary>\r
146                 /// Determine if the two entries are equal. Equality is determined\r
147                 /// by the header names being equal.\r
148                 /// </summary>\r
149                 /// <returns>\r
150                 /// True if the entries are equal.\r
151                 /// </returns>\r
152                 public override bool Equals(object it)\r
153                 {\r
154                         if (!(it is TarEntry)) \r
155                         {\r
156                                 return false;\r
157                         }\r
158                         return this.header.name.ToString().Equals(((TarEntry)it).header.name.ToString());\r
159                 }\r
160                 \r
161                 /// <summary>\r
162                 /// Must be overridden when you override Equals.\r
163                 /// </summary>\r
164                 public override int GetHashCode()\r
165                 {\r
166                         return this.header.name.ToString().GetHashCode();\r
167                 }\r
168                 \r
169                 \r
170                 /// <summary>\r
171                 /// Determine if the given entry is a descendant of this entry.\r
172                 /// Descendancy is determined by the name of the descendant\r
173                 /// starting with this entry's name.\r
174                 /// </summary>\r
175                 /// <param name = "desc">\r
176                 /// Entry to be checked as a descendent of this.\r
177                 /// </param>\r
178                 /// <returns>\r
179                 /// True if entry is a descendant of this.\r
180                 /// </returns>\r
181                 public bool IsDescendent(TarEntry desc)\r
182                 {\r
183                         return desc.header.name.ToString().StartsWith(this.header.name.ToString());\r
184                 }\r
185                 \r
186                 /// <summary>\r
187                 /// Get this entry's header.\r
188                 /// </summary>\r
189                 /// <returns>\r
190                 /// This entry's TarHeader.\r
191                 /// </returns>\r
192                 public TarHeader TarHeader \r
193                 {\r
194                         get \r
195                         {\r
196                                 return this.header;\r
197                         }\r
198                 }\r
199                 \r
200                 /// <summary>\r
201                 /// Get/Set this entry's name.\r
202                 /// </summary>\r
203                 public string Name \r
204                 {\r
205                         get \r
206                         {\r
207                                 return this.header.name.ToString();\r
208                         }\r
209                         set \r
210                         {\r
211                                 this.header.name = new StringBuilder(value);\r
212                         }\r
213                 }\r
214                 \r
215                 /// <summary>\r
216                 /// Get/set this entry's user id.\r
217                 /// </summary>\r
218                 public int UserId \r
219                 {\r
220                         get \r
221                         {\r
222                                 return this.header.userId;\r
223                         }\r
224                         set \r
225                         {\r
226                                 this.header.userId = value;\r
227                         }\r
228                 }\r
229                 \r
230                 /// <summary>\r
231                 /// Get/set this entry's group id.\r
232                 /// </summary>\r
233                 public int GroupId \r
234                 {\r
235                         get \r
236                         {\r
237                                 return this.header.groupId;\r
238                         }\r
239                         set \r
240                         {\r
241                                 this.header.groupId = value;\r
242                         }\r
243                 }\r
244                 \r
245                 /// <summary>\r
246                 /// Get/set this entry's user name.\r
247                 /// </summary>\r
248                 public string UserName \r
249                 {\r
250                         get \r
251                         {\r
252                                 return this.header.userName.ToString();\r
253                         }\r
254                         set \r
255                         {\r
256                                 this.header.userName = new StringBuilder(value);\r
257                         }\r
258                 }\r
259                 \r
260                 /// <summary>\r
261                 /// Get/set this entry's group name.\r
262                 /// </summary>\r
263                 public string GroupName \r
264                 {\r
265                         get \r
266                         {\r
267                                 return this.header.groupName.ToString();\r
268                         }\r
269                         set \r
270                         {\r
271                                 this.header.groupName = new StringBuilder(value);\r
272                         }\r
273                 }\r
274                 \r
275                 /// <summary>\r
276                 /// Convenience method to set this entry's group and user ids.\r
277                 /// </summary>\r
278                 /// <param name="userId">\r
279                 /// This entry's new user id.\r
280                 /// </param>\r
281                 /// <param name="groupId">\r
282                 /// This entry's new group id.\r
283                 /// </param>\r
284                 public void SetIds(int userId, int groupId)\r
285                 {\r
286                         UserId  = userId; \r
287                         GroupId = groupId;\r
288                 }\r
289                 \r
290                 /// <summary>\r
291                 /// Convenience method to set this entry's group and user names.\r
292                 /// </summary>\r
293                 /// <param name="userName">\r
294                 /// This entry's new user name.\r
295                 /// </param>\r
296                 /// <param name="groupName">\r
297                 /// This entry's new group name.\r
298                 /// </param>\r
299                 public void SetNames(string userName, string groupName)\r
300                 {\r
301                         UserName  = userName;\r
302                         GroupName = groupName;\r
303                 }\r
304 \r
305                 //      TODO :\r
306                 //              /**\r
307                 //              * Set this entry's modification time. The parameter passed\r
308                 //              * to this method is in "Java time".\r
309                 //              *\r
310                 //              * @param time This entry's new modification time.\r
311                 //              */\r
312                 //              public void setModTime( long time )\r
313                 //              {\r
314                 //                      this.header.modTime = time / 1000;\r
315                 //              }\r
316                 \r
317                 /// Convert time to DateTimes\r
318                 /**\r
319                 * Get/Set this entry's modification time.\r
320                 *\r
321                 * @param time This entry's new modification time.\r
322                 */\r
323                 public DateTime ModTime \r
324                 {\r
325                         get \r
326                         {\r
327                                 return this.header.modTime;\r
328                         }\r
329                         set \r
330                         {\r
331                                 this.header.modTime = value;\r
332                         }\r
333                 }\r
334                 \r
335                 /// <summary>\r
336                 /// Get this entry's file.\r
337                 /// </summary>\r
338                 /// <returns>\r
339                 /// This entry's file.\r
340                 /// </returns>\r
341                 public string File \r
342                 {\r
343                         get \r
344                         {\r
345                                 return this.file;\r
346                         }\r
347                 }\r
348                 \r
349                 /// <summary>\r
350                 /// Get/set this entry's file size.\r
351                 /// </summary>\r
352                 public long Size \r
353                 {\r
354                         get \r
355                         {\r
356                                 return this.header.size;\r
357                         }\r
358                         set \r
359                         {\r
360                                 this.header.size = value;\r
361                         }\r
362                 }\r
363                 \r
364                 /// <summary>\r
365                 /// Convenience method that will modify an entry's name directly\r
366                 /// in place in an entry header buffer byte array.\r
367                 /// </summary>\r
368                 /// <param name="outbuf">\r
369                 /// The buffer containing the entry header to modify.\r
370                 /// </param>\r
371                 /// <param name="newName">\r
372                 /// The new name to place into the header buffer.\r
373                 /// </param>\r
374                 public void AdjustEntryName(byte[] outbuf, string newName)\r
375                 {\r
376                         int offset = 0;\r
377                         offset = TarHeader.GetNameBytes(new StringBuilder(newName), outbuf, offset, TarHeader.NAMELEN);\r
378                 }\r
379                 \r
380                 /// <summary>\r
381                 /// Return whether or not this entry represents a directory.\r
382                 /// </summary>\r
383                 /// <returns>\r
384                 /// True if this entry is a directory.\r
385                 /// </returns>\r
386                 public bool IsDirectory\r
387                 {\r
388                         get \r
389                         {\r
390                                 if (this.file != null) \r
391                                 {\r
392                                         return Directory.Exists(file);\r
393                                 }\r
394                                 \r
395                                 if (this.header != null) \r
396                                 {\r
397                                         if (this.header.typeFlag == TarHeader.LF_DIR || this.header.name.ToString().EndsWith( "/" )) \r
398                                         {\r
399                                                 return true;\r
400                                         }\r
401                                 }\r
402                                 return false;\r
403                         }\r
404                 }\r
405                 \r
406                 /// <summary>\r
407                 /// Fill in a TarHeader with information from a File.\r
408                 /// </summary>\r
409                 /// <param name="hdr">\r
410                 /// The TarHeader to fill in.\r
411                 /// </param>\r
412                 /// <param name="file">\r
413                 /// The file from which to get the header information.\r
414                 /// </param>\r
415                 public void GetFileTarHeader(TarHeader hdr, string file)\r
416                 {\r
417                         this.file = file;\r
418 \r
419                         // bugfix from torhovl from #D forum:\r
420                         string name = file;\r
421 \r
422          // -jr- 23-Jan-2004 HAK HAK HAK, GnuTar allows device names in path where the name is not local to the current directory\r
423          if (Environment.CurrentDirectory == Path.GetDirectoryName(name))\r
424          {\r
425             name = Path.GetFileName(name);\r
426          }\r
427 /*                      \r
428          if (Path.DirectorySeparatorChar == '\\') \r
429                         {  // check if the OS is Windows\r
430                                 // Strip off drive letters!\r
431                                 if (name.Length > 2) \r
432                                 {\r
433                                         char ch1 = name[0];\r
434                                         char ch2 = name[1];\r
435                                         \r
436                                         if (ch2 == ':' && Char.IsLetter(ch1)) \r
437                                         {\r
438                                                 name = name.Substring(2);\r
439                                         }\r
440                                 }\r
441                         }\r
442 */\r
443                         \r
444                         name = name.Replace(Path.DirectorySeparatorChar, '/').ToLower();\r
445 \r
446                         // No absolute pathnames\r
447                         // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\",\r
448                         // so we loop on starting /'s.\r
449                         while (name.StartsWith("/")) {\r
450                                 name = name.Substring(1);\r
451                         }\r
452 \r
453                         hdr.linkName = new StringBuilder(String.Empty);\r
454                         hdr.name     = new StringBuilder(name);\r
455                         \r
456                         if (Directory.Exists(file)) {\r
457                                 hdr.mode     = 1003; // 01753 -jr- no octal constants!! 040755; // Magic number for security access for a UNIX filesystem\r
458                                 hdr.typeFlag = TarHeader.LF_DIR;\r
459                                 if (hdr.name.Length == 0 || hdr.name[hdr.name.Length - 1] != '/') {\r
460                                         hdr.name.Append("/");\r
461                                 }\r
462                                 \r
463                                 hdr.size     = 0;\r
464                         } else {\r
465                                 hdr.mode     = 33216; // 0100700 -jr-  // 0100644; // Magic number for security access for a UNIX filesystem\r
466                                 hdr.typeFlag = TarHeader.LF_NORMAL;\r
467                                 hdr.size     = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length;\r
468                         }\r
469                         \r
470                         // UNDONE When File lets us get the userName, use it!\r
471          hdr.modTime = System.IO.File.GetLastWriteTimeUtc(file.Replace('/', Path.DirectorySeparatorChar)); // -jr- Unix times are in UTC\r
472          hdr.checkSum = 0;\r
473                         hdr.devMajor = 0;\r
474                         hdr.devMinor = 0;\r
475                 }\r
476                 \r
477                 /// <summary>\r
478                 /// If this entry represents a file, and the file is a directory, return\r
479                 /// an array of TarEntries for this entry's children.\r
480                 /// </summary>\r
481                 /// <returns>\r
482                 /// An array of TarEntry's for this entry's children.\r
483                 /// </returns>\r
484                 public TarEntry[] GetDirectoryEntries()\r
485                 {\r
486                         if (this.file == null || !Directory.Exists(this.file)) \r
487                         {\r
488                                 return new TarEntry[0];\r
489                         }\r
490                         \r
491                         string[]   list   = Directory.GetFileSystemEntries(this.file);\r
492                         TarEntry[] result = new TarEntry[list.Length];\r
493 \r
494                         for (int i = 0; i < list.Length; ++i) \r
495                         {\r
496                                 result[i] = TarEntry.CreateEntryFromFile(list[i]);\r
497                         }\r
498                         \r
499                         return result;\r
500                 }\r
501                 \r
502       /// <summary>\r
503                 /// Write an entry's header information to a header buffer.\r
504                 /// </summary>\r
505                 /// <param name = "outbuf">\r
506                 /// The tar entry header buffer to fill in.\r
507                 /// </param>\r
508                 public void WriteEntryHeader(byte[] outbuf)\r
509                 {\r
510          this.header.WriteHeader(outbuf);\r
511                 }\r
512                 \r
513                 /// <summary>\r
514                 /// Fill in a TarHeader given only the entry's name.\r
515                 /// </summary>\r
516                 /// <param name="hdr">\r
517                 /// The TarHeader to fill in.\r
518                 /// </param>\r
519                 /// <param name="name">\r
520                 /// The tar entry name.\r
521                 /// </param>\r
522                 public void NameTarHeader(TarHeader hdr, string name)\r
523                 {\r
524                         bool isDir = name.EndsWith("/");   // -jr- this is true for BSD tar but not all others I think?\r
525                         \r
526                         hdr.checkSum = 0;\r
527                         \r
528                         hdr.name = new StringBuilder(name);\r
529 //                      hdr.mode = isDir ? 040755 : 0100644; // TODO : I think I've seen these magics before ...\r
530          hdr.mode = isDir ? 1003 : 33216;\r
531          hdr.userId   = 0;\r
532                         hdr.groupId  = 0;\r
533                         hdr.size     = 0;\r
534                         hdr.checkSum = 0;\r
535                         \r
536          hdr.modTime  = DateTime.UtcNow;        // -jr- 24-Jan-2004 Unix times are in utc!\r
537 //                      hdr.modTime  = DateTime.Now;   // (new java.util.Date()).getTime() / 1000;\r
538                         \r
539                         hdr.typeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;\r
540                         \r
541                         hdr.linkName  = new StringBuilder(String.Empty);\r
542                         hdr.userName  = new StringBuilder(String.Empty);\r
543                         hdr.groupName = new StringBuilder(String.Empty);\r
544                         \r
545                         hdr.devMajor = 0;\r
546                         hdr.devMinor = 0;\r
547                 }\r
548         }\r
549 }\r
550 \r
551 \r
552 \r
553 /* The original Java file had this header:\r
554         *\r
555         ** Authored by Timothy Gerard Endres\r
556         ** <mailto:time@gjt.org>  <http://www.trustice.com>\r
557         **\r
558         ** This work has been placed into the public domain.\r
559         ** You may use this work in any way and for any purpose you wish.\r
560         **\r
561         ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,\r
562         ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR\r
563         ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY\r
564         ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR\r
565         ** REDISTRIBUTION OF THIS SOFTWARE.\r
566         **\r
567         */\r