2 // Copyright (C) 2001 Mike Krueger
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 // Linking this library statically or dynamically with other modules is
19 // making a combined work based on this library. Thus, the terms and
20 // conditions of the GNU General Public License cover the whole
23 // As a special exception, the copyright holders of this library give you
24 // permission to link this library with independent modules to produce an
25 // executable, regardless of the license terms of these independent
26 // modules, and to copy and distribute the resulting executable under
27 // terms of your choice, provided that you also meet, for each linked
28 // independent module, the terms and conditions of the license of that
29 // module. An independent module is a module which is not derived from
30 // or based on this library. If you modify this library, you may extend
31 // this exception to your version of the library, but you are not
32 // obligated to do so. If you do not wish to do so, delete this
33 // exception statement from your version.
39 namespace ICSharpCode
.SharpZipLib
.Tar
43 /// This class represents an entry in a Tar archive. It consists
44 /// of the entry's header, as well as the entry's File. Entries
45 /// can be instantiated in one of three ways, depending on how
46 /// they are to be used.
48 /// TarEntries that are created from the header bytes read from
49 /// an archive are instantiated with the TarEntry( byte[] )
50 /// constructor. These entries will be used when extracting from
51 /// or listing the contents of an archive. These entries have their
52 /// header filled in using the header bytes. They also set the File
53 /// to null, since they reference an archive entry not a file.</p>
55 /// TarEntries that are created from Files that are to be written
56 /// into an archive are instantiated with the TarEntry( File )
57 /// constructor. These entries have their header filled in using
58 /// the File's information. They also keep a reference to the File
59 /// for convenience when writing entries.</p>
61 /// Finally, TarEntries can be constructed from nothing but a name.
62 /// This allows the programmer to construct the entry by hand, for
63 /// instance when only an InputStream is available for writing to
64 /// the archive, and the header information is constructed from
65 /// other information. In this case the header fields are set to
66 /// defaults and the File is set to null.</p>
68 /// <see cref="TarHeader"/>
73 /// If this entry represents a File, this references it.
78 /// This is the entry's header information.
83 /// Only Create Entries with the static CreateXYZ methods or a headerBuffer.
90 /// Construct an entry from an archive's header bytes. File is set
93 /// <param name = "headerBuf">
94 /// The header bytes from a tar archive entry.
96 public TarEntry(byte[] headerBuf
)
99 this.header
.ParseBuffer(headerBuf
);
103 public TarEntry(TarHeader header
)
106 this.header
= header
;
110 /// Construct an entry with only a name. This allows the programmer
111 /// to construct the entry's header "by hand". File is set to null.
113 public static TarEntry
CreateTarEntry(string name
)
115 TarEntry entry
= new TarEntry();
117 entry
.NameTarHeader(entry
.header
, name
);
122 /// Construct an entry for a file. File is set to file, and the
123 /// header is constructed from information from the file.
125 /// <param name = "fileName">
126 /// The file that the entry represents.
128 public static TarEntry
CreateEntryFromFile(string fileName
)
130 TarEntry entry
= new TarEntry();
132 entry
.GetFileTarHeader(entry
.header
, fileName
);
137 /// Initialization code common to all pseudo constructors.
142 this.header
= new TarHeader();
146 /// Determine if the two entries are equal. Equality is determined
147 /// by the header names being equal.
150 /// True if the entries are equal.
152 public override bool Equals(object it
)
154 if (!(it
is TarEntry
))
158 return this.header
.name
.ToString().Equals(((TarEntry
)it
).header
.name
.ToString());
162 /// Must be overridden when you override Equals.
164 public override int GetHashCode()
166 return this.header
.name
.ToString().GetHashCode();
171 /// Determine if the given entry is a descendant of this entry.
172 /// Descendancy is determined by the name of the descendant
173 /// starting with this entry's name.
175 /// <param name = "desc">
176 /// Entry to be checked as a descendent of this.
179 /// True if entry is a descendant of this.
181 public bool IsDescendent(TarEntry desc
)
183 return desc
.header
.name
.ToString().StartsWith(this.header
.name
.ToString());
187 /// Get this entry's header.
190 /// This entry's TarHeader.
192 public TarHeader TarHeader
201 /// Get/Set this entry's name.
207 return this.header
.name
.ToString();
211 this.header
.name
= new StringBuilder(value);
216 /// Get/set this entry's user id.
222 return this.header
.userId
;
226 this.header
.userId
= value;
231 /// Get/set this entry's group id.
237 return this.header
.groupId
;
241 this.header
.groupId
= value;
246 /// Get/set this entry's user name.
248 public string UserName
252 return this.header
.userName
.ToString();
256 this.header
.userName
= new StringBuilder(value);
261 /// Get/set this entry's group name.
263 public string GroupName
267 return this.header
.groupName
.ToString();
271 this.header
.groupName
= new StringBuilder(value);
276 /// Convenience method to set this entry's group and user ids.
278 /// <param name="userId">
279 /// This entry's new user id.
281 /// <param name="groupId">
282 /// This entry's new group id.
284 public void SetIds(int userId
, int groupId
)
291 /// Convenience method to set this entry's group and user names.
293 /// <param name="userName">
294 /// This entry's new user name.
296 /// <param name="groupName">
297 /// This entry's new group name.
299 public void SetNames(string userName
, string groupName
)
302 GroupName
= groupName
;
307 // * Set this entry's modification time. The parameter passed
308 // * to this method is in "Java time".
310 // * @param time This entry's new modification time.
312 // public void setModTime( long time )
314 // this.header.modTime = time / 1000;
317 /// Convert time to DateTimes
319 * Get/Set this entry's modification time.
321 * @param time This entry's new modification time.
323 public DateTime ModTime
327 return this.header
.modTime
;
331 this.header
.modTime
= value;
336 /// Get this entry's file.
339 /// This entry's file.
350 /// Get/set this entry's file size.
356 return this.header
.size
;
360 this.header
.size
= value;
365 /// Convenience method that will modify an entry's name directly
366 /// in place in an entry header buffer byte array.
368 /// <param name="outbuf">
369 /// The buffer containing the entry header to modify.
371 /// <param name="newName">
372 /// The new name to place into the header buffer.
374 public void AdjustEntryName(byte[] outbuf
, string newName
)
377 offset
= TarHeader
.GetNameBytes(new StringBuilder(newName
), outbuf
, offset
, TarHeader
.NAMELEN
);
381 /// Return whether or not this entry represents a directory.
384 /// True if this entry is a directory.
386 public bool IsDirectory
390 if (this.file
!= null)
392 return Directory
.Exists(file
);
395 if (this.header
!= null)
397 if (this.header
.typeFlag
== TarHeader
.LF_DIR
|| this.header
.name
.ToString().EndsWith( "/" ))
407 /// Fill in a TarHeader with information from a File.
409 /// <param name="hdr">
410 /// The TarHeader to fill in.
412 /// <param name="file">
413 /// The file from which to get the header information.
415 public void GetFileTarHeader(TarHeader hdr
, string file
)
419 // bugfix from torhovl from #D forum:
422 // -jr- 23-Jan-2004 HAK HAK HAK, GnuTar allows device names in path where the name is not local to the current directory
423 if (Environment
.CurrentDirectory
== Path
.GetDirectoryName(name
))
425 name
= Path
.GetFileName(name
);
428 if (Path.DirectorySeparatorChar == '\\')
429 { // check if the OS is Windows
430 // Strip off drive letters!
436 if (ch2 == ':' && Char.IsLetter(ch1))
438 name = name.Substring(2);
444 name
= name
.Replace(Path
.DirectorySeparatorChar
, '/').ToLower();
446 // No absolute pathnames
447 // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\",
448 // so we loop on starting /'s.
449 while (name
.StartsWith("/")) {
450 name
= name
.Substring(1);
453 hdr
.linkName
= new StringBuilder(String
.Empty
);
454 hdr
.name
= new StringBuilder(name
);
456 if (Directory
.Exists(file
)) {
457 hdr
.mode
= 1003; // 01753 -jr- no octal constants!! 040755; // Magic number for security access for a UNIX filesystem
458 hdr
.typeFlag
= TarHeader
.LF_DIR
;
459 if (hdr
.name
.Length
== 0 || hdr
.name
[hdr
.name
.Length
- 1] != '/') {
460 hdr
.name
.Append("/");
465 hdr
.mode
= 33216; // 0100700 -jr- // 0100644; // Magic number for security access for a UNIX filesystem
466 hdr
.typeFlag
= TarHeader
.LF_NORMAL
;
467 hdr
.size
= new FileInfo(file
.Replace('/', Path
.DirectorySeparatorChar
)).Length
;
470 // UNDONE When File lets us get the userName, use it!
471 hdr
.modTime
= System
.IO
.File
.GetLastWriteTimeUtc(file
.Replace('/', Path
.DirectorySeparatorChar
)); // -jr- Unix times are in UTC
478 /// If this entry represents a file, and the file is a directory, return
479 /// an array of TarEntries for this entry's children.
482 /// An array of TarEntry's for this entry's children.
484 public TarEntry
[] GetDirectoryEntries()
486 if (this.file
== null || !Directory
.Exists(this.file
))
488 return new TarEntry
[0];
491 string[] list
= Directory
.GetFileSystemEntries(this.file
);
492 TarEntry
[] result
= new TarEntry
[list
.Length
];
494 for (int i
= 0; i
< list
.Length
; ++i
)
496 result
[i
] = TarEntry
.CreateEntryFromFile(list
[i
]);
503 /// Write an entry's header information to a header buffer.
505 /// <param name = "outbuf">
506 /// The tar entry header buffer to fill in.
508 public void WriteEntryHeader(byte[] outbuf
)
510 this.header
.WriteHeader(outbuf
);
514 /// Fill in a TarHeader given only the entry's name.
516 /// <param name="hdr">
517 /// The TarHeader to fill in.
519 /// <param name="name">
520 /// The tar entry name.
522 public void NameTarHeader(TarHeader hdr
, string name
)
524 bool isDir
= name
.EndsWith("/"); // -jr- this is true for BSD tar but not all others I think?
528 hdr
.name
= new StringBuilder(name
);
529 // hdr.mode = isDir ? 040755 : 0100644; // TODO : I think I've seen these magics before ...
530 hdr
.mode
= isDir
? 1003 : 33216;
536 hdr
.modTime
= DateTime
.UtcNow
; // -jr- 24-Jan-2004 Unix times are in utc!
537 // hdr.modTime = DateTime.Now; // (new java.util.Date()).getTime() / 1000;
539 hdr
.typeFlag
= isDir
? TarHeader
.LF_DIR
: TarHeader
.LF_NORMAL
;
541 hdr
.linkName
= new StringBuilder(String
.Empty
);
542 hdr
.userName
= new StringBuilder(String
.Empty
);
543 hdr
.groupName
= new StringBuilder(String
.Empty
);
553 /* The original Java file had this header:
555 ** Authored by Timothy Gerard Endres
556 ** <mailto:time@gjt.org> <http://www.trustice.com>
558 ** This work has been placed into the public domain.
559 ** You may use this work in any way and for any purpose you wish.
561 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
562 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
563 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
564 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
565 ** REDISTRIBUTION OF THIS SOFTWARE.