ZipArchive.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /**
  2. // ZipArchive.m
  3. //
  4. //
  5. // Created by aish on 08-9-11.
  6. // acsolu@gmail.com
  7. // Copyright 2008 Inc. All rights reserved.
  8. //
  9. */
  10. #import "ZipArchive.h"
  11. #import "zlib.h"
  12. #import "zconf.h"
  13. #include "minizip/zip.h"
  14. #include "minizip/unzip.h"
  15. @interface NSFileManager(ZipArchive)
  16. - (NSDictionary *)_attributesOfItemAtPath:(NSString *)path followingSymLinks:(BOOL)followingSymLinks error:(NSError **)error;
  17. @end
  18. @interface ZipArchive ()
  19. -(void) OutputErrorMessage:(NSString*) msg;
  20. -(BOOL) OverWrite:(NSString*) file;
  21. -(NSDate*) Date1980;
  22. @property (nonatomic,copy) NSString* password;
  23. @end
  24. @implementation ZipArchive
  25. @synthesize delegate = _delegate;
  26. @synthesize numFiles = _numFiles;
  27. @synthesize password = _password;
  28. @synthesize unzippedFiles = _unzippedFiles;
  29. @synthesize progressBlock = _progressBlock;
  30. @synthesize stringEncoding = _stringEncoding;
  31. -(id) init
  32. {
  33. return [self initWithFileManager:[NSFileManager defaultManager]];
  34. }
  35. -(id) initWithFileManager:(NSFileManager*) fileManager
  36. {
  37. if( self=[super init] )
  38. {
  39. _zipFile = NULL;
  40. _fileManager = fileManager;
  41. self.stringEncoding = NSUTF8StringEncoding;
  42. self.compression = ZipArchiveCompressionDefault;
  43. }
  44. return self;
  45. }
  46. -(void) dealloc
  47. {
  48. // close any open file operations
  49. [self CloseZipFile2];
  50. [self UnzipCloseFile];
  51. // release retained/copied properties.
  52. [_password release];
  53. [_delegate release];
  54. [_unzippedFiles release];
  55. [super dealloc];
  56. }
  57. /**
  58. * Create a new zip file at the specified path, ready for new files to be added.
  59. *
  60. * @param zipFile the path of the zip file to create
  61. * @returns BOOL YES on success
  62. */
  63. -(BOOL) CreateZipFile2:(NSString*) zipFile
  64. {
  65. return [self CreateZipFile2:zipFile append:NO];
  66. }
  67. -(BOOL) CreateZipFile2:(NSString*) zipFile append:(BOOL)isAppend
  68. {
  69. _zipFile = zipOpen( (const char*)[zipFile UTF8String], (isAppend ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE) );
  70. if( !_zipFile )
  71. return NO;
  72. return YES;
  73. }
  74. /**
  75. * Create a new zip file at the specified path, ready for new files to be added.
  76. *
  77. * @param zipFile the path of the zip file to create
  78. * @param password a password used to encrypt the zip file
  79. * @returns BOOL YES on success
  80. */
  81. -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password
  82. {
  83. self.password = password;
  84. return [self CreateZipFile2:zipFile];
  85. }
  86. -(BOOL) CreateZipFile2:(NSString*) zipFile Password:(NSString*) password append:(BOOL)isAppend
  87. {
  88. self.password = password;
  89. return [self CreateZipFile2:zipFile append:isAppend];
  90. }
  91. /**
  92. * add an existing file on disk to the zip archive, compressing it.
  93. *
  94. * @param file the path to the file to compress
  95. * @param newname the name of the file in the zip archive, ie: path relative to the zip archive root.
  96. * @returns BOOL YES on success
  97. */
  98. -(BOOL) addFileToZip:(NSString*) file newname:(NSString*) newname;
  99. {
  100. NSData *data = [NSData dataWithContentsOfFile:file];
  101. NSError* error = nil;
  102. NSDictionary* attr = [_fileManager _attributesOfItemAtPath:file followingSymLinks:YES error:&error];
  103. BOOL result = [self addDataToZip:data fileAttributes:attr newname:newname];
  104. return result;
  105. }
  106. /**
  107. * add an existing file on disk to the zip archive, compressing it.
  108. *
  109. * @param data the data to compress
  110. * @param attr the file attribute for data to add as file
  111. * @param newname the name of the file in the zip archive, ie: path relative to the zip archive root.
  112. * @returns BOOL YES on success
  113. */
  114. -(BOOL) addDataToZip:(NSData*) data fileAttributes:(NSDictionary *)attr newname:(NSString*) newname
  115. {
  116. if (!data)
  117. {
  118. return NO;
  119. }
  120. if( !_zipFile )
  121. return NO;
  122. // tm_zip filetime;
  123. zip_fileinfo zipInfo = {{0}};
  124. NSDate* fileDate = nil;
  125. if( attr )
  126. fileDate = (NSDate*)[attr objectForKey:NSFileModificationDate];
  127. if( fileDate == nil )
  128. fileDate = [NSDate date];
  129. NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  130. NSDateComponents* components = [gregorianCalendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay |
  131. NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:fileDate];
  132. [gregorianCalendar release];
  133. zipInfo.tmz_date.tm_sec = (uInt)components.second;
  134. zipInfo.tmz_date.tm_min = (uInt)components.minute;
  135. zipInfo.tmz_date.tm_hour = (uInt)components.hour;
  136. zipInfo.tmz_date.tm_mday = (uInt)components.day;
  137. zipInfo.tmz_date.tm_mon = (uInt)components.month;
  138. zipInfo.tmz_date.tm_year = (uInt)components.year;
  139. int ret ;
  140. if( [_password length] == 0 )
  141. {
  142. ret = zipOpenNewFileInZip( _zipFile,
  143. (const char*) [newname cStringUsingEncoding:self.stringEncoding],
  144. &zipInfo,
  145. NULL,0,
  146. NULL,0,
  147. NULL,//comment
  148. Z_DEFLATED,
  149. self.compression );
  150. }
  151. else
  152. {
  153. uLong crcValue = crc32( 0L,NULL, 0L );
  154. crcValue = crc32( crcValue, (const Bytef*)[data bytes], (unsigned int)[data length] );
  155. ret = zipOpenNewFileInZip3( _zipFile,
  156. (const char*) [newname cStringUsingEncoding:self.stringEncoding],
  157. &zipInfo,
  158. NULL,0,
  159. NULL,0,
  160. NULL,//comment
  161. Z_DEFLATED,
  162. self.compression,
  163. 0,
  164. 15,
  165. 8,
  166. Z_DEFAULT_STRATEGY,
  167. [_password cStringUsingEncoding:NSASCIIStringEncoding],
  168. crcValue );
  169. }
  170. if( ret!=Z_OK )
  171. {
  172. return NO;
  173. }
  174. unsigned int dataLen = (unsigned int)[data length];
  175. ret = zipWriteInFileInZip( _zipFile, (const void*)[data bytes], dataLen);
  176. if( ret!=Z_OK )
  177. {
  178. return NO;
  179. }
  180. ret = zipCloseFileInZip( _zipFile );
  181. if( ret!=Z_OK )
  182. return NO;
  183. return YES;
  184. }
  185. /**
  186. * Close a zip file after creating and added files to it.
  187. *
  188. * @returns BOOL YES on success
  189. */
  190. -(BOOL) CloseZipFile2
  191. {
  192. self.password = nil;
  193. if( _zipFile==NULL )
  194. return NO;
  195. BOOL ret = zipClose( _zipFile,NULL )==Z_OK?YES:NO;
  196. _zipFile = NULL;
  197. return ret;
  198. }
  199. /**
  200. * open an existing zip file ready for expanding.
  201. *
  202. * @param zipFile the path to a zip file to be opened.
  203. * @returns BOOL YES on success
  204. */
  205. -(BOOL) UnzipOpenFile:(NSString*) zipFile
  206. {
  207. // create an array to receive the list of unzipped files.
  208. if (_unzippedFiles) [_unzippedFiles release];
  209. _unzippedFiles = [[NSMutableArray alloc] initWithCapacity:1];
  210. _unzFile = unzOpen( (const char*)[zipFile UTF8String] );
  211. if( _unzFile )
  212. {
  213. unz_global_info globalInfo = {0};
  214. if( unzGetGlobalInfo(_unzFile, &globalInfo )==UNZ_OK )
  215. {
  216. _numFiles = globalInfo.number_entry;
  217. NSLog(@"%lu entries in the zip file", globalInfo.number_entry);
  218. }
  219. }
  220. return _unzFile!=NULL;
  221. }
  222. /**
  223. * open an existing zip file with a password ready for expanding.
  224. *
  225. * @param zipFile the path to a zip file to be opened.
  226. * @param password the password to use decrpyting the file.
  227. * @returns BOOL YES on success
  228. */
  229. -(BOOL) UnzipOpenFile:(NSString*) zipFile Password:(NSString*) password
  230. {
  231. self.password = password;
  232. return [self UnzipOpenFile:zipFile];
  233. }
  234. /**
  235. * Expand all files in the zip archive into the specified directory.
  236. *
  237. * If a delegate has been set and responds to OverWriteOperation: it can
  238. * return YES to overwrite a file, or NO to skip that file.
  239. *
  240. * On completion, the property `unzippedFiles` will be an array populated
  241. * with the full paths of each file that was successfully expanded.
  242. *
  243. * @param path the directory where expanded files will be created
  244. * @param overwrite should existing files be overwritten
  245. * @returns BOOL YES on success
  246. */
  247. -(BOOL) UnzipFileTo:(NSString*) path overWrite:(BOOL) overwrite
  248. {
  249. BOOL success = YES;
  250. int index = 0;
  251. int progress = -1;
  252. int ret = unzGoToFirstFile( _unzFile );
  253. unsigned char buffer[4096] = {0};
  254. if( ret!=UNZ_OK )
  255. {
  256. [self OutputErrorMessage:@"Failed"];
  257. }
  258. const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
  259. do{
  260. @autoreleasepool {
  261. if( [_password length]==0 )
  262. ret = unzOpenCurrentFile( _unzFile );
  263. else
  264. ret = unzOpenCurrentFilePassword( _unzFile, password );
  265. if( ret!=UNZ_OK )
  266. {
  267. [self OutputErrorMessage:@"Error occurs"];
  268. success = NO;
  269. break;
  270. }
  271. // reading data and write to file
  272. int read ;
  273. unz_file_info fileInfo ={0};
  274. ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
  275. if( ret!=UNZ_OK )
  276. {
  277. [self OutputErrorMessage:@"Error occurs while getting file info"];
  278. success = NO;
  279. unzCloseCurrentFile( _unzFile );
  280. break;
  281. }
  282. char* filename = (char*) malloc( fileInfo.size_filename +1 );
  283. unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
  284. filename[fileInfo.size_filename] = '\0';
  285. // check if it contains directory
  286. NSString * strPath = [NSString stringWithCString:filename encoding:self.stringEncoding];
  287. BOOL isDirectory = NO;
  288. if( filename[fileInfo.size_filename-1]=='/' || filename[fileInfo.size_filename-1]=='\\')
  289. isDirectory = YES;
  290. free( filename );
  291. if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
  292. {// contains a path
  293. strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
  294. }
  295. NSString* fullPath = [path stringByAppendingPathComponent:strPath];
  296. if( isDirectory )
  297. [_fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:nil];
  298. else
  299. [_fileManager createDirectoryAtPath:[fullPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
  300. FILE* fp = NULL;
  301. do
  302. {
  303. read = unzReadCurrentFile(_unzFile, buffer, 4096);
  304. if (read >= 0)
  305. {
  306. if (fp == NULL) {
  307. if( [_fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite )
  308. {
  309. if( ![self OverWrite:fullPath] )
  310. {
  311. // don't process any more of the file, but continue
  312. break;
  313. }
  314. }
  315. if (!isDirectory) {
  316. fp = fopen( (const char*)[fullPath UTF8String], "wb");
  317. if (fp == NULL) {
  318. [self OutputErrorMessage:@"Failed to open output file for writing"];
  319. break;
  320. }
  321. }
  322. }
  323. fwrite(buffer, read, 1, fp );
  324. }
  325. else // if (read < 0)
  326. {
  327. ret = read; // result will be an error code
  328. success = NO;
  329. [self OutputErrorMessage:@"Failed to read zip file"];
  330. }
  331. } while (read > 0);
  332. if (fp)
  333. {
  334. fclose( fp );
  335. // add the full path of this file to the output array
  336. [(NSMutableArray*)_unzippedFiles addObject:fullPath];
  337. // set the orignal datetime property
  338. if( fileInfo.tmu_date.tm_year!=0 )
  339. {
  340. NSDateComponents* components = [[NSDateComponents alloc] init];
  341. components.second = fileInfo.tmu_date.tm_sec;
  342. components.minute = fileInfo.tmu_date.tm_min;
  343. components.hour = fileInfo.tmu_date.tm_hour;
  344. components.day = fileInfo.tmu_date.tm_mday;
  345. components.month = fileInfo.tmu_date.tm_mon + 1;
  346. components.year = fileInfo.tmu_date.tm_year;
  347. NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  348. NSDate* orgDate = [[gregorianCalendar dateFromComponents:components] retain];
  349. [components release];
  350. [gregorianCalendar release];
  351. NSDictionary* attr = [NSDictionary dictionaryWithObject:orgDate forKey:NSFileModificationDate]; //[_fileManager fileAttributesAtPath:fullPath traverseLink:YES];
  352. if( attr )
  353. {
  354. // [attr setValue:orgDate forKey:NSFileCreationDate];
  355. if( ![_fileManager setAttributes:attr ofItemAtPath:fullPath error:nil] )
  356. {
  357. // cann't set attributes
  358. NSLog(@"Failed to set attributes");
  359. }
  360. }
  361. [orgDate release];
  362. orgDate = nil;
  363. }
  364. }
  365. if (ret == UNZ_OK) {
  366. ret = unzCloseCurrentFile( _unzFile );
  367. if (ret != UNZ_OK) {
  368. [self OutputErrorMessage:@"file was unzipped but failed crc check"];
  369. success = NO;
  370. }
  371. }
  372. if (ret == UNZ_OK) {
  373. ret = unzGoToNextFile( _unzFile );
  374. }
  375. if (_progressBlock && _numFiles) {
  376. index++;
  377. int p = index*100/_numFiles;
  378. progress = p;
  379. _progressBlock(progress, index, _numFiles);
  380. }
  381. }
  382. } while (ret==UNZ_OK && ret!=UNZ_END_OF_LIST_OF_FILE);
  383. return success;
  384. }
  385. -(NSDictionary *)UnzipFileToMemory
  386. {
  387. NSMutableDictionary *fileDictionary = [NSMutableDictionary dictionary];
  388. BOOL success = YES;
  389. int index = 0;
  390. int progress = -1;
  391. int ret = unzGoToFirstFile( _unzFile );
  392. unsigned char buffer[4096] = {0};
  393. if( ret!=UNZ_OK )
  394. {
  395. [self OutputErrorMessage:@"Failed"];
  396. }
  397. const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
  398. do{
  399. @autoreleasepool {
  400. if( [_password length]==0 )
  401. ret = unzOpenCurrentFile( _unzFile );
  402. else
  403. ret = unzOpenCurrentFilePassword( _unzFile, password );
  404. if( ret!=UNZ_OK )
  405. {
  406. [self OutputErrorMessage:@"Error occurs"];
  407. success = NO;
  408. break;
  409. }
  410. // reading data and write to file
  411. int read ;
  412. unz_file_info fileInfo ={0};
  413. ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
  414. if( ret!=UNZ_OK )
  415. {
  416. [self OutputErrorMessage:@"Error occurs while getting file info"];
  417. success = NO;
  418. unzCloseCurrentFile( _unzFile );
  419. break;
  420. }
  421. char* filename = (char*) malloc( fileInfo.size_filename +1 );
  422. unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
  423. filename[fileInfo.size_filename] = '\0';
  424. // check if it contains directory
  425. NSString * strPath = [NSString stringWithCString:filename encoding:self.stringEncoding];
  426. free( filename );
  427. if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
  428. {// contains a path
  429. strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
  430. }
  431. NSMutableData *fileMutableData = [NSMutableData data];
  432. do
  433. {
  434. read = unzReadCurrentFile(_unzFile, buffer, 4096);
  435. if (read >= 0)
  436. {
  437. if (read != 0)
  438. {
  439. [fileMutableData appendBytes:buffer length:read];
  440. }
  441. }
  442. else // if (read < 0)
  443. {
  444. ret = read; // result will be an error code
  445. success = NO;
  446. [self OutputErrorMessage:@"Failed to read zip file"];
  447. }
  448. } while (read > 0);
  449. if (fileMutableData.length > 0)
  450. {
  451. NSData *fileData = [NSData dataWithData:fileMutableData];
  452. [fileDictionary setObject:fileData forKey:strPath];
  453. }
  454. if (ret == UNZ_OK) {
  455. ret = unzCloseCurrentFile( _unzFile );
  456. if (ret != UNZ_OK) {
  457. [self OutputErrorMessage:@"file was unzipped but failed crc check"];
  458. success = NO;
  459. }
  460. }
  461. if (ret == UNZ_OK) {
  462. ret = unzGoToNextFile( _unzFile );
  463. }
  464. if (_progressBlock && _numFiles) {
  465. index++;
  466. int p = index*100/_numFiles;
  467. progress = p;
  468. _progressBlock(progress, index, _numFiles);
  469. }
  470. }
  471. } while (ret==UNZ_OK && ret!=UNZ_END_OF_LIST_OF_FILE);
  472. NSDictionary *resultDictionary = [NSDictionary dictionaryWithDictionary:fileDictionary];
  473. return resultDictionary;
  474. }
  475. /**
  476. * Close the zip file.
  477. *
  478. * @returns BOOL YES on success
  479. */
  480. -(BOOL) UnzipCloseFile
  481. {
  482. self.password = nil;
  483. if( _unzFile ) {
  484. int err = unzClose( _unzFile );
  485. _unzFile = nil;
  486. return err ==UNZ_OK;
  487. }
  488. return YES;
  489. }
  490. /**
  491. * Return a list of filenames that are in the zip archive.
  492. * No path information is available as this can be called before the zip is expanded.
  493. *
  494. * @returns NSArray list of filenames in the zip archive.
  495. */
  496. -(NSArray*) getZipFileContents // list the contents of the zip archive. must be called after UnzipOpenFile
  497. {
  498. int ret = unzGoToFirstFile( _unzFile );
  499. NSMutableArray * allFilenames = [NSMutableArray arrayWithCapacity:40];
  500. if( ret!=UNZ_OK )
  501. {
  502. [self OutputErrorMessage:@"Failed"];
  503. }
  504. const char* password = [_password cStringUsingEncoding:NSASCIIStringEncoding];
  505. do{
  506. if( [_password length]==0 )
  507. ret = unzOpenCurrentFile( _unzFile );
  508. else
  509. ret = unzOpenCurrentFilePassword( _unzFile, password );
  510. if( ret!=UNZ_OK )
  511. {
  512. [self OutputErrorMessage:@"Error occured"];
  513. break;
  514. }
  515. // reading data and write to file
  516. unz_file_info fileInfo ={0};
  517. ret = unzGetCurrentFileInfo(_unzFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
  518. if( ret!=UNZ_OK )
  519. {
  520. [self OutputErrorMessage:@"Error occurs while getting file info"];
  521. unzCloseCurrentFile( _unzFile );
  522. break;
  523. }
  524. char* filename = (char*) malloc( fileInfo.size_filename +1 );
  525. unzGetCurrentFileInfo(_unzFile, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
  526. filename[fileInfo.size_filename] = '\0';
  527. // check if it contains directory
  528. NSString * strPath = [NSString stringWithCString:filename encoding:NSASCIIStringEncoding];
  529. free( filename );
  530. if( [strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location!=NSNotFound )
  531. {// contains a path
  532. strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
  533. }
  534. // Copy name to array
  535. [allFilenames addObject:strPath];
  536. unzCloseCurrentFile( _unzFile );
  537. ret = unzGoToNextFile( _unzFile );
  538. } while( ret==UNZ_OK && UNZ_OK!=UNZ_END_OF_LIST_OF_FILE );
  539. // return an immutable array.
  540. return [NSArray arrayWithArray:allFilenames];
  541. }
  542. #pragma mark wrapper for delegate
  543. /**
  544. * send the ErrorMessage: to the delegate if it responds to it.
  545. */
  546. -(void) OutputErrorMessage:(NSString*) msg
  547. {
  548. if( _delegate && [_delegate respondsToSelector:@selector(ErrorMessage:)] )
  549. [_delegate ErrorMessage:msg];
  550. }
  551. /**
  552. * send the OverWriteOperation: selector to the delegate if it responds to it,
  553. * returning the result, or YES by default.
  554. */
  555. -(BOOL) OverWrite:(NSString*) file
  556. {
  557. if( _delegate && [_delegate respondsToSelector:@selector(OverWriteOperation:)] )
  558. return [_delegate OverWriteOperation:file];
  559. return YES;
  560. }
  561. #pragma mark get NSDate object for 1980-01-01
  562. -(NSDate*) Date1980
  563. {
  564. NSDateComponents *comps = [[NSDateComponents alloc] init];
  565. [comps setDay:1];
  566. [comps setMonth:1];
  567. [comps setYear:1980];
  568. NSCalendar *gregorian = [[NSCalendar alloc]
  569. initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  570. NSDate *date = [gregorian dateFromComponents:comps];
  571. [comps release];
  572. [gregorian release];
  573. return date;
  574. }
  575. @end
  576. @implementation NSFileManager(ZipArchive)
  577. - (NSDictionary *)_attributesOfItemAtPath:(NSString *)path followingSymLinks:(BOOL)followingSymLinks error:(NSError **)error
  578. {
  579. // call file manager default action, which is to not follow symlinks
  580. NSDictionary* results = [self attributesOfItemAtPath:path error:error];
  581. if (followingSymLinks && results && (error ? *error == nil : YES)) {
  582. if ([[results fileType] isEqualToString:NSFileTypeSymbolicLink]) {
  583. // follow the symlink
  584. NSString* realPath = [self destinationOfSymbolicLinkAtPath:path error:error];
  585. if (realPath && (error ? *error == nil : YES)) {
  586. return [self _attributesOfItemAtPath:realPath followingSymLinks:followingSymLinks error:error];
  587. } else {
  588. // failure to resolve symlink should be an error returning nil and error will already be set.
  589. return nil;
  590. }
  591. }
  592. }
  593. return results;
  594. }
  595. @end