1 // ZipEntry.cs 2 // 3 // Copyright (C) 2001 Mike Krueger 4 // Copyright (C) 2004 John Reilly 5 // 6 // This file was translated from java, it was part of the GNU Classpath 7 // Copyright (C) 2001 Free Software Foundation, Inc. 8 // 9 // This program is free software; you can redistribute it and/or 10 // modify it under the terms of the GNU General Public License 11 // as published by the Free Software Foundation; either version 2 12 // of the License, or (at your option) any later version. 13 // 14 // This program is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with this program; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 // 23 // Linking this library statically or dynamically with other modules is 24 // making a combined work based on this library. Thus, the terms and 25 // conditions of the GNU General Public License cover the whole 26 // combination. 27 // 28 // As a special exception, the copyright holders of this library give you 29 // permission to link this library with independent modules to produce an 30 // executable, regardless of the license terms of these independent 31 // modules, and to copy and distribute the resulting executable under 32 // terms of your choice, provided that you also meet, for each linked 33 // independent module, the terms and conditions of the license of that 34 // module. An independent module is a module which is not derived from 35 // or based on this library. If you modify this library, you may extend 36 // this exception to your version of the library, but you are not 37 // obligated to do so. If you do not wish to do so, delete this 38 // exception statement from your version. 39 40 using System; 41 using System.IO; 42 43 namespace ICSharpCode.SharpZipLib.Zip 44 { 45 46 /// <summary> 47 /// This class represents an entry in a zip archive. This can be a file 48 /// or a directory 49 /// ZipFile and ZipInputStream will give you instances of this class as 50 /// information about the members in an archive. ZipOutputStream 51 /// uses an instance of this class when creating an entry in a Zip file. 52 /// <br/> 53 /// <br/>Author of the original java version : Jochen Hoenicke 54 /// </summary> 55 [System.ObsoleteAttribute("This assembly has been deprecated. Please use https://www.nuget.org/packages/SharpZipLib/ instead.")] 56 public class ZipEntry : ICloneable 57 { 58 static int KNOWN_SIZE = 1; 59 static int KNOWN_CSIZE = 2; 60 static int KNOWN_CRC = 4; 61 static int KNOWN_TIME = 8; 62 static int KNOWN_EXTERN_ATTRIBUTES = 16; 63 64 ushort known = 0; // Bit flags made up of above bits 65 int externalFileAttributes = -1; // contains external attributes (os dependant) 66 67 ushort versionMadeBy; // Contains host system and version information 68 // only relevant for central header entries 69 70 string name; 71 ulong size; 72 ulong compressedSize; 73 ushort versionToExtract; // Version required to extract (library handles <= 2.0) 74 uint crc; 75 uint dosTime; 76 77 CompressionMethod method = CompressionMethod.Deflated; 78 byte[] extra = null; 79 string comment = null; 80 81 int flags; // general purpose bit flags 82 83 int zipFileIndex = -1; // used by ZipFile 84 int offset; // used by ZipFile and ZipOutputStream 85 86 /// <summary> 87 /// Get/Set flag indicating if entry is encrypted. 88 /// A simple helper routine to aid interpretation of <see cref="Flags">flags</see> 89 /// </summary> 90 public bool IsCrypted { 91 get { 92 return (flags & 1) != 0; 93 } 94 set { 95 if (value) { 96 flags |= 1; 97 } else { 98 flags &= ~1; 99 } 100 } 101 } 102 103 /// <summary> 104 /// Get/Set general purpose bit flag for entry 105 /// </summary> 106 /// <remarks> 107 /// General purpose bit flag<br/> 108 /// Bit 0: If set, indicates the file is encrypted<br/> 109 /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating<br/> 110 /// Imploding:<br/> 111 /// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used<br/> 112 /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise<br/> 113 /// <br/> 114 /// Deflating:<br/> 115 /// Bit 2 Bit 1<br/> 116 /// 0 0 Normal compression was used<br/> 117 /// 0 1 Maximum compression was used<br/> 118 /// 1 0 Fast compression was used<br/> 119 /// 1 1 Super fast compression was used<br/> 120 /// <br/> 121 /// Bit 3: If set, the fields crc-32, compressed size 122 /// and uncompressed size are were not able to be written during zip file creation 123 /// The correct values are held in a data descriptor immediately following the compressed data. <br/> 124 /// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/> 125 /// Bit 5: If set indicates the file contains compressed patch data<br/> 126 /// Bit 6: If set indicates strong encryption was used.<br/> 127 /// Bit 7-15: Unused or reserved<br/> 128 /// </remarks> 129 public int Flags { 130 get { 131 return flags; 132 } 133 set { 134 flags = value; 135 } 136 } 137 138 139 /// <summary> 140 /// Get/Set index of this entry in Zip file 141 /// </summary> 142 public int ZipFileIndex { 143 get { 144 return zipFileIndex; 145 } 146 set { 147 zipFileIndex = value; 148 } 149 } 150 151 /// <summary> 152 /// Get/set offset for use in central header 153 /// </summary> 154 public int Offset { 155 get { 156 return offset; 157 } 158 set { 159 if (((ulong)value & 0xFFFFFFFF00000000L) != 0) { 160 throw new ArgumentOutOfRangeException("Offset"); 161 } 162 offset = value; 163 } 164 } 165 166 167 /// <summary> 168 /// Get/Set external file attributes as an integer. 169 /// The values of this are operating system dependant see 170 /// <see cref="HostSystem">HostSystem</see> for details 171 /// </summary> 172 public int ExternalFileAttributes { 173 get { 174 if ((known & KNOWN_EXTERN_ATTRIBUTES) == 0) { 175 return -1; 176 } else { 177 return externalFileAttributes; 178 } 179 } 180 181 set { 182 externalFileAttributes = value; 183 known |= (ushort)KNOWN_EXTERN_ATTRIBUTES; 184 } 185 } 186 187 /// <summary> 188 /// Get the version made by for this entry or zero if unknown. 189 /// The value / 10 indicates the major version number, and 190 /// the value mod 10 is the minor version number 191 /// </summary> 192 public int VersionMadeBy { 193 get { 194 return versionMadeBy & 0xff; 195 } 196 } 197 198 /// <summary> 199 /// Gets the compatability information for the <see cref="ExternalFileAttributes">external file attribute</see> 200 /// If the external file attributes are compatible with MS-DOS and can be read 201 /// by PKZIP for DOS version 2.04g then this value will be zero. Otherwise the value 202 /// will be non-zero and identify the host system on which the attributes are compatible. 203 /// </summary> 204 /// 205 /// <remarks> 206 /// The values for this as defined in the Zip File format and by others are shown below. The values are somewhat 207 /// misleading in some cases as they are not all used as shown. You should consult the relevant documentation 208 /// to obtain up to date and correct information. The modified appnote by the infozip group is 209 /// particularly helpful as it documents a lot of peculiarities. The document is however a little dated. 210 /// <list type="table"> 211 /// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item> 212 /// <item>1 - Amiga</item> 213 /// <item>2 - OpenVMS</item> 214 /// <item>3 - Unix</item> 215 /// <item>4 - VM/CMS</item> 216 /// <item>5 - Atari ST</item> 217 /// <item>6 - OS/2 HPFS</item> 218 /// <item>7 - Macintosh</item> 219 /// <item>8 - Z-System</item> 220 /// <item>9 - CP/M</item> 221 /// <item>10 - Windows NTFS</item> 222 /// <item>11 - MVS (OS/390 - Z/OS)</item> 223 /// <item>12 - VSE</item> 224 /// <item>13 - Acorn Risc</item> 225 /// <item>14 - VFAT</item> 226 /// <item>15 - Alternate MVS</item> 227 /// <item>16 - BeOS</item> 228 /// <item>17 - Tandem</item> 229 /// <item>18 - OS/400</item> 230 /// <item>19 - OS/X (Darwin)</item> 231 /// <item>99 - WinZip AES</item> 232 /// <item>remainder - unused</item> 233 /// </list> 234 /// </remarks> 235 236 public int HostSystem { 237 get { return (versionMadeBy >> 8) & 0xff; } 238 } 239 240 /// <summary> 241 /// Creates a zip entry with the given name. 242 /// </summary> 243 /// <param name="name"> 244 /// The name for this entry. Can include directory components. 245 /// The convention for names is 'unix' style paths with no device names and 246 /// path elements separated by '/' characters. This is not enforced see <see cref="CleanName">CleanName</see> 247 /// on how to ensure names are valid if this is desired. 248 /// </param> 249 /// <exception cref="ArgumentNullException"> 250 /// The name passed is null 251 /// </exception> ZipEntry(string name)252 public ZipEntry(string name) : this(name, 0, ZipConstants.VERSION_MADE_BY) 253 { 254 } 255 256 /// <summary> 257 /// Creates a zip entry with the given name and version required to extract 258 /// </summary> 259 /// <param name="name"> 260 /// The name for this entry. Can include directory components. 261 /// The convention for names is 'unix' style paths with no device names and 262 /// path elements separated by '/' characters. This is not enforced see <see cref="CleanName">CleanName</see> 263 /// on how to ensure names are valid if this is desired. 264 /// </param> 265 /// <param name="versionRequiredToExtract"> 266 /// The minimum 'feature version' required this entry 267 /// </param> 268 /// <exception cref="ArgumentNullException"> 269 /// The name passed is null 270 /// </exception> ZipEntry(string name, int versionRequiredToExtract)271 internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, ZipConstants.VERSION_MADE_BY) 272 { 273 } 274 275 /// <summary> 276 /// Initializes an entry with the given name and made by information 277 /// </summary> 278 /// <param name="name">Name for this entry</param> 279 /// <param name="madeByInfo">Version and HostSystem Information</param> 280 /// <param name="versionRequiredToExtract">Minimum required zip feature version required to extract this entry</param> 281 /// <exception cref="ArgumentNullException"> 282 /// The name passed is null 283 /// </exception> 284 /// <exception cref="ArgumentOutOfRangeException"> 285 /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 286 /// </exception> 287 /// <remarks> 288 /// This constructor is used by the ZipFile class when reading from the central header 289 /// It is not generally useful, use the constructor specifying the name only. 290 /// </remarks> ZipEntry(string name, int versionRequiredToExtract, int madeByInfo)291 internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo) 292 { 293 if (name == null) { 294 throw new System.ArgumentNullException("ZipEntry name"); 295 } 296 297 if ( name.Length == 0 ) { 298 throw new ArgumentException("ZipEntry name is empty"); 299 } 300 301 if (versionRequiredToExtract != 0 && versionRequiredToExtract < 10) { 302 throw new ArgumentOutOfRangeException("versionRequiredToExtract"); 303 } 304 305 this.DateTime = System.DateTime.Now; 306 this.name = name; 307 this.versionMadeBy = (ushort)madeByInfo; 308 this.versionToExtract = (ushort)versionRequiredToExtract; 309 } 310 311 /// <summary> 312 /// Creates a copy of the given zip entry. 313 /// </summary> 314 /// <param name="e"> 315 /// The entry to copy. 316 /// </param> ZipEntry(ZipEntry e)317 public ZipEntry(ZipEntry e) 318 { 319 known = e.known; 320 name = e.name; 321 size = e.size; 322 compressedSize = e.compressedSize; 323 crc = e.crc; 324 dosTime = e.dosTime; 325 method = e.method; 326 ExtraData = e.ExtraData; // Note use of property ensuring data is unique 327 comment = e.comment; 328 versionToExtract = e.versionToExtract; 329 versionMadeBy = e.versionMadeBy; 330 externalFileAttributes = e.externalFileAttributes; 331 flags = e.flags; 332 333 zipFileIndex = -1; 334 offset = 0; 335 } 336 337 /// <summary> 338 /// Get minimum Zip feature version required to extract this entry 339 /// </summary> 340 /// <remarks> 341 /// Minimum features are defined as:<br/> 342 /// 1.0 - Default value<br/> 343 /// 1.1 - File is a volume label<br/> 344 /// 2.0 - File is a folder/directory<br/> 345 /// 2.0 - File is compressed using Deflate compression<br/> 346 /// 2.0 - File is encrypted using traditional encryption<br/> 347 /// 2.1 - File is compressed using Deflate64<br/> 348 /// 2.5 - File is compressed using PKWARE DCL Implode<br/> 349 /// 2.7 - File is a patch data set<br/> 350 /// 4.5 - File uses Zip64 format extensions<br/> 351 /// 4.6 - File is compressed using BZIP2 compression<br/> 352 /// 5.0 - File is encrypted using DES<br/> 353 /// 5.0 - File is encrypted using 3DES<br/> 354 /// 5.0 - File is encrypted using original RC2 encryption<br/> 355 /// 5.0 - File is encrypted using RC4 encryption<br/> 356 /// 5.1 - File is encrypted using AES encryption<br/> 357 /// 5.1 - File is encrypted using corrected RC2 encryption<br/> 358 /// 5.1 - File is encrypted using corrected RC2-64 encryption<br/> 359 /// 6.1 - File is encrypted using non-OAEP key wrapping<br/> 360 /// 6.2 - Central directory encryption (not confirmed yet)<br/> 361 /// </remarks> 362 public int Version { 363 get { 364 if (versionToExtract != 0) { 365 return versionToExtract; 366 } else { 367 int result = 10; 368 if (CompressionMethod.Deflated == method) { 369 result = 20; 370 } else if (IsDirectory == true) { 371 result = 20; 372 } else if (IsCrypted == true) { 373 result = 20; 374 } else if ((known & KNOWN_EXTERN_ATTRIBUTES) != 0 && (externalFileAttributes & 0x08) != 0) { 375 result = 11; 376 } 377 return result; 378 } 379 } 380 } 381 382 /// <summary> 383 /// Gets a value indicating if the entry requires Zip64 extensions to be stored 384 /// </summary> 385 public bool RequiresZip64 { 386 get { 387 return (this.size > uint.MaxValue) || (this.compressedSize > uint.MaxValue); 388 } 389 } 390 391 /// <summary> 392 /// Get/Set DosTime 393 /// </summary> 394 public long DosTime { 395 get { 396 if ((known & KNOWN_TIME) == 0) { 397 return 0; 398 } else { 399 return dosTime; 400 } 401 } 402 set { 403 this.dosTime = (uint)value; 404 known |= (ushort)KNOWN_TIME; 405 } 406 } 407 408 409 /// <summary> 410 /// Gets/Sets the time of last modification of the entry. 411 /// </summary> 412 public DateTime DateTime { 413 get { 414 // Although technically not valid some archives have dates set to zero. 415 // This mimics some archivers handling and is a good a cludge as any probably. 416 if ( dosTime == 0 ) { 417 return DateTime.Now; 418 } 419 else { 420 uint sec = 2 * (dosTime & 0x1f); 421 uint min = (dosTime >> 5) & 0x3f; 422 uint hrs = (dosTime >> 11) & 0x1f; 423 uint day = (dosTime >> 16) & 0x1f; 424 uint mon = ((dosTime >> 21) & 0xf); 425 uint year = ((dosTime >> 25) & 0x7f) + 1980; 426 return new System.DateTime((int)year, (int)mon, (int)day, (int)hrs, (int)min, (int)sec); 427 } 428 } 429 set { 430 DosTime = ((uint)value.Year - 1980 & 0x7f) << 25 | 431 ((uint)value.Month) << 21 | 432 ((uint)value.Day) << 16 | 433 ((uint)value.Hour) << 11 | 434 ((uint)value.Minute) << 5 | 435 ((uint)value.Second) >> 1; 436 } 437 } 438 439 /// <summary> 440 /// Returns the entry name. The path components in the entry should 441 /// always separated by slashes ('/'). Dos device names like C: should also 442 /// be removed. See <see cref="CleanName">CleanName</see>. 443 /// </summary> 444 public string Name { 445 get { 446 return name; 447 } 448 } 449 450 /// <summary> 451 /// Cleans a name making it conform to Zip file conventions. 452 /// Devices names ('c:\') and UNC share names ('\\server\share') are removed 453 /// and forward slashes ('\') are converted to back slashes ('/'). 454 /// </summary> 455 /// <param name="name">Name to clean</param> 456 /// <param name="relativePath">Make names relative if true or absolute if false</param> CleanName(string name, bool relativePath)457 static public string CleanName(string name, bool relativePath) 458 { 459 if (name == null) { 460 return ""; 461 } 462 463 if (Path.IsPathRooted(name) == true) { 464 // NOTE: 465 // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt 466 name = name.Substring(Path.GetPathRoot(name).Length); 467 } 468 469 name = name.Replace(@"\", "/"); 470 471 if (relativePath == true) { 472 if (name.Length > 0 && (name[0] == Path.AltDirectorySeparatorChar || name[0] == Path.DirectorySeparatorChar)) { 473 name = name.Remove(0, 1); 474 } 475 } else { 476 if (name.Length > 0 && name[0] != Path.AltDirectorySeparatorChar && name[0] != Path.DirectorySeparatorChar) { 477 name = name.Insert(0, "/"); 478 } 479 } 480 return name; 481 } 482 483 /// <summary> 484 /// Cleans a name making it conform to Zip file conventions. 485 /// Devices names ('c:\') and UNC share names ('\\server\share') are removed 486 /// and forward slashes ('\') are converted to back slashes ('/'). 487 /// Names are made relative by trimming leading slashes which is compatible 488 /// with Windows-XPs built in Zip file handling. 489 /// </summary> 490 /// <param name="name">Name to clean</param> CleanName(string name)491 static public string CleanName(string name) 492 { 493 return CleanName(name, true); 494 } 495 496 /// <summary> 497 /// Gets/Sets the size of the uncompressed data. 498 /// </summary> 499 /// <exception cref="System.ArgumentOutOfRangeException"> 500 /// If the size is not in the range 0..0xffffffffL 501 /// </exception> 502 /// <returns> 503 /// The size or -1 if unknown. 504 /// </returns> 505 public long Size { 506 get { 507 return (known & KNOWN_SIZE) != 0 ? (long)size : -1L; 508 } 509 set { 510 if (((ulong)value & 0xFFFFFFFF00000000L) != 0) { 511 throw new ArgumentOutOfRangeException("size"); 512 } 513 this.size = (ulong)value; 514 this.known |= (ushort)KNOWN_SIZE; 515 } 516 } 517 518 /// <summary> 519 /// Gets/Sets the size of the compressed data. 520 /// </summary> 521 /// <exception cref="System.ArgumentOutOfRangeException"> 522 /// Size is not in the range 0..0xffffffff 523 /// </exception> 524 /// <returns> 525 /// The size or -1 if unknown. 526 /// </returns> 527 public long CompressedSize { 528 get { 529 return (known & KNOWN_CSIZE) != 0 ? (long)compressedSize : -1L; 530 } 531 set { 532 if (((ulong)value & 0xffffffff00000000L) != 0) { 533 throw new ArgumentOutOfRangeException(); 534 } 535 this.compressedSize = (ulong)value; 536 this.known |= (ushort)KNOWN_CSIZE; 537 } 538 } 539 540 /// <summary> 541 /// Gets/Sets the crc of the uncompressed data. 542 /// </summary> 543 /// <exception cref="System.ArgumentOutOfRangeException"> 544 /// Crc is not in the range 0..0xffffffffL 545 /// </exception> 546 /// <returns> 547 /// The crc value or -1 if unknown. 548 /// </returns> 549 public long Crc { 550 get { 551 return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; 552 } 553 set { 554 if (((ulong)crc & 0xffffffff00000000L) != 0) { 555 throw new ArgumentOutOfRangeException(); 556 } 557 this.crc = (uint)value; 558 this.known |= (ushort)KNOWN_CRC; 559 } 560 } 561 562 /// <summary> 563 /// Gets/Sets the compression method. Only Deflated and Stored are supported. 564 /// </summary> 565 /// <returns> 566 /// The compression method for this entry 567 /// </returns> 568 /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Deflated"/> 569 /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Stored"/> 570 public CompressionMethod CompressionMethod { 571 get { 572 return method; 573 } 574 set { 575 this.method = value; 576 } 577 } 578 579 /// <summary> 580 /// Gets/Sets the extra data. 581 /// </summary> 582 /// <exception cref="System.ArgumentOutOfRangeException"> 583 /// Extra data is longer than 0xffff bytes. 584 /// </exception> 585 /// <returns> 586 /// Extra data or null if not set. 587 /// </returns> 588 public byte[] ExtraData { 589 get { 590 return extra; 591 } 592 set { 593 if (value == null) { 594 this.extra = null; 595 return; 596 } 597 598 if (value.Length > 0xffff) { 599 throw new System.ArgumentOutOfRangeException(); 600 } 601 602 this.extra = new byte[value.Length]; 603 Array.Copy(value, 0, this.extra, 0, value.Length); 604 605 try { 606 int pos = 0; 607 while (pos < extra.Length) { 608 int sig = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8; 609 int len = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8; 610 611 if (len < 0 || pos + len > extra.Length) { 612 // This is still lenient but the extra data is corrupt 613 // TODO: drop the extra data? or somehow indicate to user 614 // there is a problem... 615 break; 616 } 617 618 if (sig == 0x5455) { 619 // extended time stamp, unix format by Rainer Prem <Rainer@Prem.de> 620 int flags = extra[pos]; 621 // Can include other times but these are ignored. Length of data should 622 // actually be 1 + 4 * no of bits in flags. 623 if ((flags & 1) != 0 && len >= 5) { 624 int iTime = ((extra[pos+1] & 0xff) | 625 (extra[pos + 2] & 0xff) << 8 | 626 (extra[pos + 3] & 0xff) << 16 | 627 (extra[pos + 4] & 0xff) << 24); 628 629 DateTime = (new DateTime ( 1970, 1, 1, 0, 0, 0 ) + new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime (); 630 known |= (ushort)KNOWN_TIME; 631 } 632 } else if (sig == 0x0001) { 633 // ZIP64 extended information extra field 634 // Of variable size depending on which fields in header are too small 635 // fields appear here if the corresponding local or central directory record field 636 // is set to 0xFFFF or 0xFFFFFFFF and the entry is in Zip64 format. 637 // 638 // Original Size 8 bytes 639 // Compressed size 8 bytes 640 // Relative header offset 8 bytes 641 // Disk start number 4 bytes 642 } 643 pos += len; 644 } 645 } catch (Exception) { 646 /* be lenient */ 647 return; 648 } 649 } 650 } 651 652 653 /// <summary> 654 /// Gets/Sets the entry comment. 655 /// </summary> 656 /// <exception cref="System.ArgumentOutOfRangeException"> 657 /// If comment is longer than 0xffff. 658 /// </exception> 659 /// <returns> 660 /// The comment or null if not set. 661 /// </returns> 662 public string Comment { 663 get { 664 return comment; 665 } 666 set { 667 // TODO: this test is strictly incorrect as the length is in characters 668 // While the test is correct in that a comment of this length or greater 669 // is definitely invalid, shorter comments may also have an invalid length. 670 if (value != null && value.Length > 0xffff) { 671 throw new ArgumentOutOfRangeException(); 672 } 673 this.comment = value; 674 } 675 } 676 677 /// <summary> 678 /// Gets a value indicating of the if the entry is a directory. A directory is determined by 679 /// an entry name with a trailing slash '/'. The external file attributes 680 /// can also mark a file as a directory. The trailing slash convention should always be followed 681 /// however. 682 /// </summary> 683 public bool IsDirectory { 684 get { 685 int nlen = name.Length; 686 bool result = nlen > 0 && name[nlen - 1] == '/'; 687 688 if (result == false && (known & KNOWN_EXTERN_ATTRIBUTES) != 0) { 689 if (HostSystem == 0 && (ExternalFileAttributes & 16) != 0) { 690 result = true; 691 } 692 } 693 return result; 694 } 695 } 696 697 /// <summary> 698 /// Get a value of true if the entry appears to be a file; false otherwise 699 /// </summary> 700 /// <remarks> 701 /// This only takes account Windows attributes. Other operating systems are ignored. 702 /// For linux and others the result may be incorrect. 703 /// </remarks> 704 public bool IsFile { 705 get { 706 bool result = !IsDirectory; 707 708 // Exclude volume labels 709 if ( result && (known & KNOWN_EXTERN_ATTRIBUTES) != 0) { 710 if (HostSystem == 0 && (ExternalFileAttributes & 8) != 0) { 711 result = false; 712 } 713 } 714 return result; 715 } 716 } 717 718 /// <summary> 719 /// Creates a copy of this zip entry. 720 /// </summary> Clone()721 public object Clone() 722 { 723 return this.MemberwiseClone(); 724 } 725 726 /// <summary> 727 /// Gets the string representation of this ZipEntry. 728 /// </summary> ToString()729 public override string ToString() 730 { 731 return name; 732 } 733 } 734 } 735