]> err.no Git - sope/blob - libFoundation/Foundation/NSFileManager.m
fixed some NGMail framework build issue
[sope] / libFoundation / Foundation / NSFileManager.m
1 /* 
2    NSFileManager.m
3
4    Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
5    All rights reserved.
6
7    Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
8            Ovidiu Predescu <ovidiu@bx.logicnet.ro>
9            Helge Hess <helge@mdlink.de>
10
11    This file is part of libFoundation.
12
13    Permission to use, copy, modify, and distribute this software and its
14    documentation for any purpose and without fee is hereby granted, provided
15    that the above copyright notice appear in all copies and that both that
16    copyright notice and this permission notice appear in supporting
17    documentation.
18
19    We disclaim all warranties with regard to this software, including all
20    implied warranties of merchantability and fitness, in no event shall
21    we be liable for any special, indirect or consequential damages or any
22    damages whatsoever resulting from loss of use, data or profits, whether in
23    an action of contract, negligence or other tortious action, arising out of
24    or in connection with the use or performance of this software.
25 */
26
27 #include <Foundation/common.h>
28 #include <objc/objc.h>
29
30 #include <stdio.h>
31
32 /* determine directory reading files */
33
34 #if defined(HAVE_DIRENT_H)
35 #  include <dirent.h>
36 #elif defined(HAVE_SYS_DIR_H)
37 #  include <sys/dir.h>
38 #elif defined(HAVE_SYS_NDIR_H)
39 #  include <sys/ndir.h>
40 #elif defined(HAVE_NDIR_H)
41 #  include <ndir.h>
42 #elif defined(HAVE_DIR_H)
43 #  include <dir.h>
44 #endif
45
46 #if defined(HAVE_WINDOWS_H)
47 #  include <windows.h>
48 #endif
49
50 #if !defined(_POSIX_VERSION)
51 #  if defined(NeXT)
52 #    define DIR_enum_item struct direct
53 #  endif
54 #endif
55
56 #if !defined(DIR_enum_item)
57 #  define DIR_enum_item struct dirent
58 #endif
59
60 #define DIR_enum_state DIR
61
62 /* determine filesystem max path length */
63
64 #if defined(_POSIX_VERSION) || defined(__WIN32__)
65 # include <limits.h>                    /* for PATH_MAX */
66 # if defined(__MINGW32__)
67 #   include <sys/utime.h>
68 # else
69 #   include <utime.h>
70 # endif
71 #else
72 # if HAVE_SYS_PARAM_H
73 #  include <sys/param.h>                /* for MAXPATHLEN */
74 # endif
75 #endif
76
77 #ifndef PATH_MAX
78 # ifdef _POSIX_VERSION
79 #  define PATH_MAX _POSIX_PATH_MAX
80 # else
81 #  ifdef MAXPATHLEN
82 #   define PATH_MAX MAXPATHLEN
83 #  else
84 #   define PATH_MAX 1024
85 #  endif
86 # endif
87 #endif
88
89 /* determine if we have statfs struct and function */
90
91 #ifdef HAVE_SYS_STATFS_H
92 # include <sys/statfs.h>
93 #endif
94
95 #ifdef HAVE_SYS_STATVFS_H
96 # include <sys/statvfs.h>
97 #endif
98
99 #ifdef HAVE_SYS_VFS_H
100 # include <sys/vfs.h>
101 #endif
102
103 #if HAVE_SYS_FILE_H
104 #include <sys/file.h>
105 #endif
106
107 #if HAVE_SYS_STAT_H
108 # include <sys/stat.h>
109 #endif
110
111 #include <fcntl.h>
112
113 #if HAVE_UTIME_H
114 # include <utime.h>
115 #endif
116
117 /* include usual headers */
118
119 #include <Foundation/NSArray.h>
120 #include <Foundation/NSDictionary.h>
121 #include <Foundation/NSData.h>
122 #include <Foundation/NSDate.h>
123 #include <Foundation/NSString.h>
124 #include <Foundation/NSValue.h>
125 #include <Foundation/NSAutoreleasePool.h>
126 #include <Foundation/NSPathUtilities.h>
127 #include <Foundation/NSThread.h>
128 #include <Foundation/NSLock.h>
129 #include <Foundation/NSNotification.h>
130 #include <Foundation/NSFileManager.h>
131 #include <Foundation/NSException.h>
132 #include <Foundation/exceptions/GeneralExceptions.h>
133
134 @interface NSFileManager (PrivateMethods)
135
136 /* Copies the contents of source file to destination file. Assumes source
137    and destination are regular files or symbolic links. */
138 - (BOOL)_copyFile:(NSString*)source toFile:(NSString*)destination
139   handler:handler;
140
141 /* Recursively copies the contents of source directory to destination. */
142 - (BOOL)_copyPath:(NSString*)source toPath:(NSString*)destination
143   handler:handler;
144
145 @end /* NSFileManager (PrivateMethods) */
146
147
148 /*
149  * NSFileManager implementation
150  */
151
152 @implementation NSFileManager
153
154 // Getting the default manager
155
156 static BOOL isMultithreaded = NO;
157 static NSFileManager* defaultManager = nil;
158
159 + (void)initialize
160 {
161     static BOOL initialized = NO;
162
163     if (!initialized) {
164         defaultManager = [[self alloc] init];
165         [[NSNotificationCenter defaultCenter]
166             addObserver:self
167             selector:@selector(taskNowMultiThreaded:)
168             name:NSWillBecomeMultiThreadedNotification
169             object:nil];
170         initialized = YES;
171     }
172 }
173
174 + (void)taskNowMultiThreaded:notification
175 {
176     NSThread* currentThread = [NSThread currentThread];
177
178     [[currentThread threadDictionary]
179         setObject:AUTORELEASE(defaultManager) forKey:@"DefaultNSFileManager"];
180     defaultManager = nil;
181     isMultithreaded = YES;
182 }
183
184 extern NSRecursiveLock* libFoundationLock;
185
186 + (NSFileManager*)defaultManager
187 {
188     if (isMultithreaded) {
189         NSThread* currentThread = [NSThread currentThread];
190         id manager;
191
192         [libFoundationLock lock];
193         {
194             manager = [[currentThread threadDictionary]
195                                       objectForKey:@"DefaultNSFileManager"];
196             if (!manager) {
197                 manager = AUTORELEASE([[self alloc] init]);
198                 [[currentThread threadDictionary]
199                                 setObject:manager
200                                 forKey:@"DefaultNSFileManager"];
201             }
202         }
203         [libFoundationLock unlock];
204
205         return manager;
206     } 
207     else
208         return defaultManager;
209 }
210
211 // Directory operations
212
213 - (BOOL)changeCurrentDirectoryPath:(NSString*)path
214 {
215     const char* cpath = [self fileSystemRepresentationWithPath:path];
216
217 #if defined(__MINGW32__)
218     return SetCurrentDirectory(cpath) == TRUE ? YES : NO;
219 #else
220     return (chdir(cpath) == 0);
221 #endif
222 }
223
224 #if defined(__MINGW32__)
225 - (BOOL)createDirectoryAtPath:(NSString*)path
226   attributes:(NSDictionary*)attributes
227 {
228     NSEnumerator *paths = [[path pathComponents] objectEnumerator];
229     NSString     *subPath;
230     NSString     *completePath = nil;
231
232     while ((subPath = [paths nextObject])) {
233         BOOL isDir = NO;
234
235         completePath = (completePath == nil)
236             ? subPath
237             : [completePath stringByAppendingPathComponent:subPath];
238
239         if ([self fileExistsAtPath:completePath isDirectory:&isDir]) {
240             if (!isDir) {
241                 fprintf(stderr,
242                         "WARNING: during creation of directory %s:"
243                         " sub path %s exists, but is not a directory !",
244                       [path cString], [completePath cString]);
245             }
246         }
247         else {
248             const char *cpath;
249             
250             cpath = [self fileSystemRepresentationWithPath:completePath];
251             if (CreateDirectory(cpath, NULL) == FALSE)
252                 // creation failed
253                 return NO;
254         }
255     }
256
257     // change attributes of last directory
258     return [self changeFileAttributes:attributes
259                  atPath:path];
260 }
261
262 #else
263
264 - (BOOL)createDirectoryAtPath:(NSString*)path
265   attributes:(NSDictionary*)attributes
266 {
267     const char* cpath;
268     char        dirpath[PATH_MAX+1];
269     struct stat statbuf;
270     int         len, cur;
271     
272     cpath = [self fileSystemRepresentationWithPath:path];
273     len = Strlen(cpath);
274     if (len > PATH_MAX)
275         // name too long
276         return NO;
277     
278     if (Strcmp(cpath, "/") == 0 || len == 0)
279         // cannot use "/" or "" as a new dir path
280         return NO;
281     
282     strcpy(dirpath, cpath);
283     dirpath[len] = '\0';
284     if (dirpath[len-1] == '/')
285         dirpath[len-1] = '\0';
286     cur = 0;
287     
288     do {
289         // find next path separator
290         while (dirpath[cur] != '/' && cur < len)
291             cur++;
292
293         // if first char is '/' then again; (cur == len) -> last component
294         if (cur == 0) {
295             cur++;
296             continue;
297         }
298         // check if path from 0 to cur is valid
299         dirpath[cur] = '\0';
300         if (stat(dirpath, &statbuf) == 0) {
301             if (cur == len)
302                 return NO; // already existing last path
303         }
304         else {
305             // make new directory
306 #if MKDIR_HAS_TWO_ARGS || defined(__CYGWIN32__)
307             if (mkdir(dirpath, 0777) != 0)
308 #else
309             if (mkdir(dirpath) != 0)
310 #endif
311                 return NO; // could not create component
312             // if last directory and attributes then change
313             if (cur == len && attributes)
314                 return [self changeFileAttributes:attributes 
315                     atPath:[self stringWithFileSystemRepresentation:dirpath
316                         length:cur]];
317         }
318         dirpath[cur] = '/';
319         cur++;
320     } while (cur < len);
321     
322     return YES;
323 }
324 #endif /* __MINGW32__ */
325
326 - (NSString*)currentDirectoryPath
327 {
328 #if defined(__MINGW32__)
329     unsigned char *buf = objc_atomic_malloc(2048);
330     DWORD         len = GetCurrentDirectory(2046, buf);
331
332     if (len > 2046) {
333         buf = objc_realloc(buf, len + 2);
334         len = GetCurrentDirectory(len, buf);
335     }
336     if (len == 0) return nil;
337     self = [NSString stringWithCString:buf length:len];
338     objc_free(buf);
339     return self;
340 #else
341     char path[PATH_MAX];
342 #if defined(HAVE_GETCWD)
343     if (getcwd(path, PATH_MAX-1) == NULL)
344         return nil;
345 #else
346     if (getwd(path) == NULL)
347         return nil;
348 #endif /* HAVE_GETCWD */
349     return [self stringWithFileSystemRepresentation:path length:Strlen(path)];
350 #endif /* __MINGW32__ */
351 }
352
353 // File operations
354
355 - (BOOL)copyPath:(NSString*)source toPath:(NSString*)destination
356   handler:handler
357 {
358     BOOL         sourceIsDir;
359     NSDictionary *attributes;
360
361     if (![self fileExistsAtPath:source isDirectory:&sourceIsDir])
362         // source must exist
363         return NO;
364
365     if ([self fileExistsAtPath:destination])
366         // destination must not exist
367         return NO;
368
369 #if defined(__MINGW32__)
370     if (!CopyFile([self fileSystemRepresentationWithPath:source],
371                   [self fileSystemRepresentationWithPath:destination],
372                   FALSE /* overwrite if dest exists */)) {
373         if (handler) {
374             NSDictionary *errorInfo
375                 = [NSDictionary dictionaryWithObjectsAndKeys:
376                                 source,              @"Path",
377                                 destination,         @"ToPath",
378                                 @"cannot copy file", @"Error",
379                                 nil];
380             if ([handler fileManager:self shouldProceedAfterError:errorInfo])
381                 return YES;
382         }
383         return NO;
384     }
385     return YES;
386 #else
387     attributes = [self fileAttributesAtPath:source traverseLink:NO];
388
389     if (sourceIsDir) {
390         /* If destination directory is a descendant of source directory copying
391             isn't possible. */
392         if ([[destination stringByAppendingString:@"/"]
393                           hasPrefix:[source stringByAppendingString:@"/"]])
394             return NO;
395
396         [handler fileManager:self willProcessPath:destination];
397         if (![self createDirectoryAtPath:destination attributes:attributes]) {
398             if (handler) {
399                 NSDictionary* errorInfo
400                     = [NSDictionary dictionaryWithObjectsAndKeys:
401                         destination,                @"Path",
402                         @"cannot create directory", @"Error",
403                         nil];
404                 return [handler fileManager:self
405                                 shouldProceedAfterError:errorInfo];
406             }
407             else
408                 return NO;
409         }
410     }
411
412     if (sourceIsDir) {
413         if (![self _copyPath:source toPath:destination handler:handler])
414             return NO;
415         else {
416             [self changeFileAttributes:attributes atPath:destination];
417             return YES;
418         }
419     }
420     else {
421         [handler fileManager:self willProcessPath:source];
422         if (![self _copyFile:source toFile:destination handler:handler])
423             return NO;
424         else {
425             [self changeFileAttributes:attributes atPath:destination];
426             return YES;
427         }
428     }
429     return NO;
430 #endif
431 }
432
433 - (BOOL)movePath:(NSString*)source toPath:(NSString*)destination 
434   handler:(id)handler
435 {
436     BOOL       sourceIsDir;
437     const char *sourcePath;
438     const char *destPath;
439 #if !defined(__MINGW32__)
440     NSString* destinationParent;
441     unsigned int sourceDevice, destinationDevice;
442 #endif
443
444     sourcePath = [self fileSystemRepresentationWithPath:source];
445     destPath   = [self fileSystemRepresentationWithPath:destination];
446     
447     if (![self fileExistsAtPath:source isDirectory:&sourceIsDir])
448         // source does not exist
449         return NO;
450
451     if ([self fileExistsAtPath:destination])
452         // destination does already exist
453         return NO;
454
455 #if defined(__MINGW32__)
456     /*
457       Special handling for directories is required !
458       (See MoveFile on msdn)
459     */
460     if (!MoveFile(sourcePath, destPath)) {
461         if (handler) {
462             NSDictionary *errorInfo
463                 = [NSDictionary dictionaryWithObjectsAndKeys:
464                                 source,              @"Path",
465                                 destination,         @"ToPath",
466                                 @"cannot move file", @"Error",
467                                 nil];
468             if ([handler fileManager:self shouldProceedAfterError:errorInfo])
469                 return YES;
470         }
471         return NO;
472     }
473     return YES;
474 #else
475     /* Check to see if the source and destination's parent are on the same
476        physical device so we can perform a rename syscall directly. */
477     sourceDevice = [[[self fileSystemAttributesAtPath:source]
478                             objectForKey:NSFileSystemNumber]
479                             unsignedIntValue];
480     destinationParent = [destination stringByDeletingLastPathComponent];
481     if ([destinationParent isEqual:@""])
482         destinationParent = @".";
483     destinationDevice
484         = [[[self fileSystemAttributesAtPath:destinationParent]
485                   objectForKey:NSFileSystemNumber]
486                   unsignedIntValue];
487
488     if (sourceDevice != destinationDevice) {
489         /* If destination directory is a descendant of source directory moving
490             isn't possible. */
491         if (sourceIsDir && [[destination stringByAppendingString:@"/"]
492                             hasPrefix:[source stringByAppendingString:@"/"]])
493             return NO;
494
495         if ([self copyPath:source toPath:destination handler:handler]) {
496             NSDictionary* attributes;
497
498             attributes = [self fileAttributesAtPath:source traverseLink:NO];
499             [self changeFileAttributes:attributes atPath:destination];
500             return [self removeFileAtPath:source handler:handler];
501         }
502         else
503             return NO;
504     }
505     else {
506         /* source and destination are on the same device so we can simply
507            invoke rename on source. */
508         [handler fileManager:self willProcessPath:source];
509         if (rename (sourcePath, destPath) == -1) {
510             if (handler) {
511                 NSDictionary* errorInfo
512                     = [NSDictionary dictionaryWithObjectsAndKeys:
513                         source, @"Path",
514                         destination, @"ToPath",
515                         @"cannot move file", @"Error",
516                         nil];
517                 if ([handler fileManager:self
518                              shouldProceedAfterError:errorInfo])
519                     return YES;
520             }
521             return NO;
522         }
523         return YES;
524     }
525 #endif
526     return NO;
527 }
528
529 - (BOOL)linkPath:(NSString*)source toPath:(NSString*)destination
530   handler:handler
531 {
532     // TODO
533     [self notImplemented:_cmd];
534     return NO;
535 }
536
537 - (BOOL)removeFileAtPath:(NSString *)path
538   handler:(id)handler
539 {
540     // TODO: this method should be cleaned up !!!
541     NSDirectoryEnumerator *enumerator;
542     NSString              *dirEntry;
543     NSString              *completeFilename;
544     NSString              *fileType;
545     NSDictionary          *attributes;
546     const char            *cpath;
547     BOOL                  pathIsDir, fileExists;
548     CREATE_AUTORELEASE_POOL(pool);
549     
550     if (path == nil)
551         [[InvalidArgumentException new] raise];
552     
553     if ([path isEqual:@"."] || [path isEqual:@".."])
554         [[InvalidArgumentException new] raise];
555
556     fileExists = [self fileExistsAtPath:path isDirectory:&pathIsDir];
557     if (!fileExists)
558         return NO;
559
560     [handler fileManager:self willProcessPath:path];
561
562     if (!pathIsDir) {
563         cpath = [self fileSystemRepresentationWithPath:path];
564
565 #if defined(__MINGW32__)
566         if (DeleteFile(cpath) == FALSE)
567 #else
568         if (unlink(cpath)) /* use unlink, we know it's a file */
569 #endif
570         {
571             if (handler) {
572                 NSDictionary *errorInfo;
573                 errorInfo =
574                     [NSDictionary dictionaryWithObjectsAndKeys:
575                                     path ? path : @"<nil>", @"Path",
576                                     @"cannot remove file", @"Error",
577                                     nil];
578                 if (![handler fileManager:self
579                               shouldProceedAfterError:errorInfo])
580                     return NO;
581
582                 /* intended fall-through ? [is this really correct?] */
583             }
584             else
585                 return NO;
586         }
587         else
588             return YES;
589     }
590     else {
591         enumerator = [self enumeratorAtPath:path];
592         while ((dirEntry = [enumerator nextObject])) {
593         attributes = [enumerator fileAttributes];
594         fileType = [attributes objectForKey:NSFileType];
595         completeFilename = [path stringByAppendingPathComponent:dirEntry];
596     
597         if ([fileType isEqual:NSFileTypeDirectory]) {
598             /* Skip the descendants of this directory so they will not be
599                present in further steps. */
600             [enumerator skipDescendents];
601         }
602     
603         if (![self removeFileAtPath:completeFilename handler:handler])
604             return NO;
605         }
606     
607     
608         if (rmdir([self fileSystemRepresentationWithPath:path])) {
609             if (handler) {
610                 NSDictionary *errorInfo;
611     
612                     errorInfo =
613                     [NSDictionary dictionaryWithObjectsAndKeys:
614                                         path ? path : @"<nil>", @"Path",
615                                         @"cannot remove directory", @"Error",
616                                         nil];
617                 if (![handler fileManager:self
618                               shouldProceedAfterError:errorInfo])
619                     return NO;
620             }
621             else
622                 return NO;
623         }
624     }
625     
626     RELEASE(pool);
627
628     return YES;
629 }
630
631 - (BOOL)createFileAtPath:(NSString*)path contents:(NSData*)contents
632   attributes:(NSDictionary*)attributes
633 {
634 #if defined(__MINGW32__)
635     HANDLE fh;
636
637     fh = CreateFile([self fileSystemRepresentationWithPath:path],
638                     GENERIC_WRITE,
639                     0,    // fdwShareMode
640                     NULL, // security attributes
641                     CREATE_ALWAYS,
642                     FILE_ATTRIBUTE_NORMAL,
643                     NULL);
644     if (fh == INVALID_HANDLE_VALUE)
645         return NO;
646     else {
647         DWORD len     = [contents length];
648         DWORD written = 0;
649
650         if (len)
651             WriteFile(fh, [contents bytes], len, &written, NULL);
652         CloseHandle(fh);
653
654         if (![self changeFileAttributes:attributes atPath:path])
655             return NO;
656
657         return written == len ? YES : NO;
658     }
659 #else
660     int fd, len, written;
661
662     fd = open ([self fileSystemRepresentationWithPath:path],
663                 O_WRONLY|O_TRUNC|O_CREAT, 0644);
664     if (fd < 0)
665         return NO;
666
667     if (![self changeFileAttributes:attributes atPath:path]) {
668         close (fd);
669         return NO;
670     }
671
672     len = [contents length];
673     if (len)
674         written = write (fd, [contents bytes], len);
675     else
676         written = 0;
677     close (fd);
678
679     return written == len;
680 #endif
681 }
682
683 // Getting and comparing file contents
684
685 - (NSData*)contentsAtPath:(NSString*)path
686 {
687     return [NSData dataWithContentsOfFile:path];
688 }
689
690 - (BOOL)contentsEqualAtPath:(NSString*)path1 andPath:(NSString*)path2
691 {
692     // TODO
693     [self notImplemented:_cmd];
694     return NO;
695 }
696
697 // Detemining access to files
698
699 - (BOOL)fileExistsAtPath:(NSString*)path
700 {
701     return [self fileExistsAtPath:path isDirectory:NULL];
702 }
703
704 #if defined(__MINGW32__)
705 - (BOOL)fileExistsAtPath:(NSString*)path isDirectory:(BOOL*)isDirectory
706 {
707     DWORD result;
708     if (path == NULL) return NO;
709     result = GetFileAttributes([self fileSystemRepresentationWithPath:path]);
710     if (result == -1)
711         return NO;
712
713     if (isDirectory)
714         *isDirectory = (result & FILE_ATTRIBUTE_DIRECTORY) ? YES : NO;
715     return YES;
716 }
717
718 - (BOOL)isReadableFileAtPath:(NSString*)path
719 {
720     DWORD result;
721     if (path == NULL) return NO;
722     result = GetFileAttributes([self fileSystemRepresentationWithPath:path]);
723     if (result == -1)
724         return NO;
725     return YES;
726 }
727 - (BOOL)isWritableFileAtPath:(NSString*)path
728 {
729     DWORD result;
730     if (path == NULL) return NO;
731     result = GetFileAttributes([self fileSystemRepresentationWithPath:path]);
732     if (result == -1)
733         return NO;
734
735     return (result & FILE_ATTRIBUTE_READONLY) ? NO : YES;
736 }
737 - (BOOL)isExecutableFileAtPath:(NSString*)path
738 {
739     // naive, is there a better way ?
740     if ([self isReadableFileAtPath:path]) {
741         return [[path pathExtension] isEqualToString:@"exe"];
742     }
743     else
744         return NO;
745 }
746 - (BOOL)isDeletableFileAtPath:(NSString*)path
747 {
748     // TODO - handle directories
749     return [self isWritableFileAtPath:path];
750 }
751
752 #else
753 - (BOOL)fileExistsAtPath:(NSString*)path isDirectory:(BOOL*)isDirectory
754 {
755     struct stat statbuf;
756     const char* cpath = [self fileSystemRepresentationWithPath:path];
757
758     if (stat(cpath, &statbuf) != 0)
759         return NO;
760     
761     if (isDirectory)
762         *isDirectory = ((statbuf.st_mode & S_IFMT) == S_IFDIR);
763     return YES;
764 }
765
766 - (BOOL)isReadableFileAtPath:(NSString*)path
767 {
768     const char* cpath = [self fileSystemRepresentationWithPath:path];
769     
770     return (access(cpath, R_OK) == 0);
771 }
772
773 - (BOOL)isWritableFileAtPath:(NSString*)path
774 {
775     const char* cpath = [self fileSystemRepresentationWithPath:path];
776     
777     return (access(cpath, W_OK) == 0);
778 }
779
780 - (BOOL)isExecutableFileAtPath:(NSString*)path
781 {
782     const char* cpath = [self fileSystemRepresentationWithPath:path];
783     
784     return (access(cpath, X_OK) == 0);
785 }
786
787 - (BOOL)isDeletableFileAtPath:(NSString*)path
788 {
789     // TODO - handle directories
790     const char* cpath;
791     
792     cpath = [self fileSystemRepresentationWithPath:
793         [path stringByDeletingLastPathComponent]];
794     
795     if (access(cpath, X_OK | W_OK) != 0)
796         return NO;
797
798     cpath = [self fileSystemRepresentationWithPath:path];
799
800     return  (access(cpath, X_OK | W_OK) == 0);
801 }
802 #endif    
803
804 - (NSDictionary*)fileAttributesAtPath:(NSString*)path traverseLink:(BOOL)flag
805 {
806     struct stat statbuf;
807     const char* cpath = [self fileSystemRepresentationWithPath:path];
808     int mode;
809 #if HAVE_GETPWUID
810     struct passwd *pw;
811 #endif
812     int count = 10;
813
814     id  values[11];
815     id  keys[11] = {
816         NSFileSize,
817         NSFileModificationDate,
818         NSFileOwnerAccountNumber,
819         NSFileGroupOwnerAccountNumber,
820         NSFileReferenceCount,
821         NSFileIdentifier,
822         NSFileDeviceIdentifier,
823         NSFilePosixPermissions,
824         NSFileType,
825         NSFileOwnerAccountName
826     };
827
828 #ifdef S_IFLNK
829     if (flag) {
830         /* traverseLink */
831         if (stat(cpath, &statbuf) != 0)
832             return nil;
833     }
834     else {
835         /* do not traverseLink */
836         if (lstat(cpath, &statbuf) != 0)
837             return nil;
838     }
839 #else
840     if (stat(cpath, &statbuf) != 0)
841         return nil;
842 #endif
843     
844     values[0] = [NSNumber numberWithUnsignedLongLong:statbuf.st_size];
845     values[1] = [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtime];
846     values[2] = [NSNumber numberWithUnsignedInt:statbuf.st_uid];
847     values[3] = [NSNumber numberWithUnsignedInt:statbuf.st_gid];
848     values[4] = [NSNumber numberWithUnsignedInt:statbuf.st_nlink];
849     values[5] = [NSNumber numberWithUnsignedLong:statbuf.st_ino];
850     values[6] = [NSNumber numberWithUnsignedInt:statbuf.st_dev];
851     values[7] = [NSNumber numberWithUnsignedInt:statbuf.st_mode];
852     
853     mode = statbuf.st_mode & S_IFMT;
854
855     if      (mode == S_IFREG)  values[8] = NSFileTypeRegular;
856     else if (mode == S_IFDIR)  values[8] = NSFileTypeDirectory;
857     else if (mode == S_IFCHR)  values[8] = NSFileTypeCharacterSpecial;
858     else if (mode == S_IFBLK)  values[8] = NSFileTypeBlockSpecial;
859 #ifdef S_IFLNK
860     else if (mode == S_IFLNK)  values[8] = NSFileTypeSymbolicLink;
861 #endif
862     else if (mode == S_IFIFO)  values[8] = NSFileTypeFifo;
863 #ifdef S_IFSOCK
864     else if (mode == S_IFSOCK) values[8] = NSFileTypeSocket;
865 #endif
866     else                       values[8] = NSFileTypeUnknown;
867     count = 9;
868     
869 #if HAVE_GETPWUID
870     pw = getpwuid(statbuf.st_uid);
871     
872     if (pw) {
873         values[count] = [NSString stringWithCString:pw->pw_name];
874         count++;
875     }
876 #endif
877     
878     return AUTORELEASE([[NSDictionary alloc]
879                            initWithObjects:values forKeys:keys count:count]);
880 }
881
882 - (NSDictionary*)fileSystemAttributesAtPath:(NSString*)path
883 {
884 #if HAVE_SYS_VFS_H || HAVE_SYS_STATFS_H
885     struct stat statbuf;
886 #if HAVE_STATVFS
887     struct statvfs statfsbuf;
888 #else
889     struct statfs statfsbuf;
890 #endif
891     long long totalsize, freesize;
892     const char* cpath = [self fileSystemRepresentationWithPath:path];
893     
894     id  values[5];
895     id  keys[5] = {
896         NSFileSystemSize,
897         NSFileSystemFreeSize,
898         NSFileSystemNodes,
899         NSFileSystemFreeNodes,
900         NSFileSystemNumber
901     };
902     
903     if (stat(cpath, &statbuf) != 0)
904         return nil;
905
906 #if HAVE_STATVFS
907     if (statvfs(cpath, &statfsbuf) != 0)
908         return nil;
909 #else
910     if (statfs(cpath, &statfsbuf) != 0)
911         return nil;
912 #endif
913
914     totalsize = statfsbuf.f_bsize * statfsbuf.f_blocks;
915     freesize = statfsbuf.f_bsize * statfsbuf.f_bfree;
916     
917     values[0] = [NSNumber numberWithLongLong:totalsize];
918     values[1] = [NSNumber numberWithLongLong:freesize];
919     values[2] = [NSNumber numberWithLong:statfsbuf.f_files];
920     values[3] = [NSNumber numberWithLong:statfsbuf.f_ffree];
921     values[4] = [NSNumber numberWithUnsignedInt:statbuf.st_dev];
922     
923     return AUTORELEASE([[NSDictionary alloc]
924                            initWithObjects:values forKeys:keys count:5]);
925 #else
926     return nil;
927 #endif
928 }
929
930 - (BOOL)changeFileAttributes:(NSDictionary*)attributes atPath:(NSString*)path
931 {
932     const char* cpath = [self fileSystemRepresentationWithPath:path];
933     NSNumber* num;
934     NSDate* date;
935     BOOL allOk = YES;
936     
937 #if HAVE_CHOWN
938     num = [attributes objectForKey:NSFileOwnerAccountNumber];
939     if (num) {
940         allOk &= (chown(cpath, [num intValue], -1) == 0);
941     }
942     
943     num = [attributes objectForKey:NSFileGroupOwnerAccountNumber];
944     if (num) {
945         allOk &= (chown(cpath, -1, [num intValue]) == 0);
946     }
947 #endif
948     
949     num = [attributes objectForKey:NSFilePosixPermissions];
950     if (num) {
951         allOk &= (chmod(cpath, [num intValue]) == 0);
952     }
953     
954     date = [attributes objectForKey:NSFileModificationDate];
955     if (date) {
956         struct stat sb;
957 #if defined(_POSIX_VERSION) || defined(__WIN32__)
958         struct utimbuf ub;
959 #else
960         time_t ub[2];
961 #endif
962
963         if (stat(cpath, &sb) != 0)
964             allOk = NO;
965         else {
966 #if defined(_POSIX_VERSION) || defined(__WIN32__)
967             ub.actime = sb.st_atime;
968             ub.modtime = [date timeIntervalSince1970];
969             allOk &= (utime(cpath, &ub) == 0);
970 #else
971             ub[0] = sb.st_atime;
972             ub[1] = [date timeIntervalSince1970];
973             allOk &= (utime((char*)cpath, ub) == 0);
974 #endif
975         }
976     }
977     
978     return allOk;
979 }
980
981 // Discovering directory contents
982
983 - (NSArray *)directoryContentsAtPath:(NSString *)path
984 {
985     NSDirectoryEnumerator* direnum;
986     NSMutableArray* content;
987     BOOL isDir;
988     
989     if (![self fileExistsAtPath:path isDirectory:&isDir] || !isDir)
990         return nil;
991     
992     direnum = [[NSDirectoryEnumerator alloc]
993         initWithDirectoryPath:path 
994         recurseIntoSubdirectories:NO
995         followSymlinks:NO
996         prefixFiles:NO];
997     content = AUTORELEASE([[NSMutableArray alloc] init]);
998     
999     while ((path = [direnum nextObject]))
1000         [content addObject:path];
1001
1002     RELEASE(direnum); direnum = nil;
1003
1004     return content;
1005 }
1006
1007 - (NSDirectoryEnumerator*)enumeratorAtPath:(NSString*)path
1008 {
1009     return AUTORELEASE([[NSDirectoryEnumerator alloc]
1010                            initWithDirectoryPath:path 
1011                            recurseIntoSubdirectories:YES
1012                            followSymlinks:NO
1013                            prefixFiles:YES]);
1014 }
1015
1016 - (NSArray*)subpathsAtPath:(NSString*)path
1017 {
1018     NSDirectoryEnumerator* direnum;
1019     NSMutableArray* content;
1020     BOOL isDir;
1021     
1022     if (![self fileExistsAtPath:path isDirectory:&isDir] || !isDir)
1023         return nil;
1024     
1025     direnum = [[NSDirectoryEnumerator alloc]
1026         initWithDirectoryPath:path 
1027         recurseIntoSubdirectories:YES
1028         followSymlinks:NO
1029         prefixFiles:YES];
1030     content = AUTORELEASE([[NSMutableArray alloc] init]);
1031     
1032     while ((path = [direnum nextObject]))
1033         [content addObject:path];
1034
1035     RELEASE(direnum); direnum = nil;
1036
1037     return content;
1038 }
1039
1040 // Symbolic-link operations
1041
1042 - (BOOL)createSymbolicLinkAtPath:(NSString*)path
1043   pathContent:(NSString*)otherPath
1044 {
1045 #if HAVE_SYMLINK
1046     const char* lpath = [self fileSystemRepresentationWithPath:path];
1047     const char* npath = [self fileSystemRepresentationWithPath:otherPath];
1048     
1049     return (symlink(lpath, npath) == 0);
1050 #else
1051         [[InvalidArgumentException new] raise];
1052     return NO;
1053 #endif
1054 }
1055
1056 - (NSString*)pathContentOfSymbolicLinkAtPath:(NSString*)path
1057 {
1058 #if HAVE_READLINK
1059     char  lpath[PATH_MAX];
1060     const char* cpath = [self fileSystemRepresentationWithPath:path];
1061     int   llen = readlink(cpath, lpath, PATH_MAX-1);
1062     
1063     if (llen > 0)
1064         return [self stringWithFileSystemRepresentation:lpath length:llen];
1065     else
1066 #endif
1067         return nil;
1068 }
1069
1070 // Converting file-system representations
1071
1072 - (const char*)fileSystemRepresentationWithPath:(NSString*)path
1073 {
1074     return [path cString];
1075 }
1076
1077 - (NSString*)stringWithFileSystemRepresentation:(const char*)string
1078   length:(unsigned int)len
1079 {
1080     return [NSString stringWithCString:string length:len];
1081 }
1082
1083 @end /* NSFileManager */
1084
1085 /*
1086  * NSDirectoryEnumerator implementation
1087  */
1088
1089 @implementation NSDirectoryEnumerator
1090
1091 #if defined(__MINGW32__)
1092
1093 typedef struct _MingDIR {
1094     HANDLE          handle;
1095     WIN32_FIND_DATA info;
1096     int             dirCount;
1097 } MingDIR;
1098
1099 static inline MingDIR *ming_opendir(const char *cstr) {
1100     DWORD result;
1101     
1102     if (cstr == NULL)
1103         return NULL;
1104
1105     result = GetFileAttributes(cstr);
1106     if (result == 0xFFFFFFFF) {
1107         NSLog(@"ERROR: could not get file attributes of path '%s'", cstr);
1108         return NULL;
1109     }
1110
1111     if (result & FILE_ATTRIBUTE_DIRECTORY) {
1112         MingDIR *dir = objc_atomic_malloc(sizeof(MingDIR));
1113         int     len  = strlen(cstr);
1114         char    *buf = objc_atomic_malloc(len + 10);
1115
1116         strcpy(buf, cstr);
1117         if (len > 0) {
1118             if (buf[len - 1] == '\\')
1119                 strcat(buf, "*");
1120             else
1121                 strcat(buf, "\\*");
1122         }
1123         else
1124             strcat(buf, "\\*");
1125
1126         dir->dirCount = 0;
1127         dir->handle = FindFirstFile(buf, &(dir->info));
1128         objc_free(buf); buf = NULL;
1129         if (dir->handle == INVALID_HANDLE_VALUE) {
1130             objc_free(dir); dir = NULL;
1131             return NULL;
1132         }
1133         return dir;
1134     }
1135     else {
1136         // not a directory
1137         NSLog(@"ERROR: path '%s' is not a directory !", cstr);
1138         return NULL;
1139     }
1140 }
1141 static inline void ming_closedir(MingDIR *dir) {
1142     if (dir) {
1143         if (dir->handle != INVALID_HANDLE_VALUE) {
1144             FindClose(dir->handle);
1145             dir->handle = INVALID_HANDLE_VALUE;
1146         }
1147         free(dir);
1148     }
1149 }
1150
1151 static inline WIN32_FIND_DATA *ming_readdir(MingDIR *dir) {
1152     if (dir->dirCount == 0) {
1153         // first entry
1154         dir->dirCount += 1;
1155         return &(dir->info);
1156     }
1157     else if (dir->handle != INVALID_HANDLE_VALUE) {
1158         if (FindNextFile(dir->handle, &(dir->info)) == FALSE) {
1159             FindClose(dir->handle);
1160             dir->handle = INVALID_HANDLE_VALUE;
1161             return NULL;
1162         }
1163         dir->dirCount += 1;
1164         return &(dir->info);
1165     }
1166     else // directory closed
1167         return NULL;
1168 }
1169
1170 #endif
1171
1172 // Implementation dependent methods
1173
1174 /* 
1175   recurses into directory `path' 
1176         - pushes relative path (relative to root of search) on pathStack
1177         - pushes system dir enumerator on enumPath 
1178 */
1179 - (void)recurseIntoDirectory:(NSString*)path relativeName:(NSString*)name
1180 {
1181     const char* cpath;
1182 #if defined(__MINGW32__)
1183     MingDIR *dir;
1184 #elif HAVE_OPENDIR
1185     DIR *dir;
1186 #endif
1187
1188     cpath = [[NSFileManager defaultManager]
1189                             fileSystemRepresentationWithPath:path];
1190
1191     if (cpath == NULL)
1192         [[InvalidArgumentException new] raise];
1193
1194 #if defined(__MINGW32__)
1195     if ((dir = ming_opendir(cpath))) {
1196          [pathStack addObject:name];
1197          [enumStack addObject:[NSValue valueWithPointer:dir]];
1198     }
1199 #elif HAVE_OPENDIR
1200     if ((dir = opendir(cpath))) {
1201         [pathStack addObject:name];
1202         [enumStack addObject:[NSValue valueWithPointer:dir]];
1203     }
1204 #else
1205     [[InvalidArgumentException new] raise];
1206 #endif
1207 }
1208
1209 /*
1210   backtracks enumeration to the previous dir
1211         - pops current dir relative path from pathStack
1212         - pops system dir enumerator from enumStack
1213         - sets currentFile* to nil
1214 */
1215 - (void)backtrack
1216 {
1217 #if defined(__MINGW32__)
1218     ming_closedir((MingDIR *)[[enumStack lastObject] pointerValue]);
1219 #elif HAVE_OPENDIR
1220     closedir((DIR *)[[enumStack lastObject] pointerValue]);
1221 #else
1222     [[InvalidArgumentException new] raise];
1223 #endif
1224     [enumStack removeLastObject];
1225     [pathStack removeLastObject];
1226     RELEASE(currentFileName); currentFileName = nil;
1227     RELEASE(currentFilePath); currentFilePath = nil;
1228 }
1229
1230 /*
1231   finds the next file according to the top enumerator
1232         - if there is a next file it is put in currentFile
1233         - if the current file is a directory and if isRecursive calls 
1234             recurseIntoDirectory:currentFile
1235         - if the current file is a symlink to a directory and if isRecursive 
1236             and isFollowing calls recurseIntoDirectory:currentFile
1237         - if at end of current directory pops stack and attempts to
1238             find the next entry in the parent
1239         - sets currentFile to nil if there are no more files to enumerate
1240 */
1241 - (void)findNextFile
1242 {
1243     NSFileManager   *manager = [NSFileManager defaultManager];
1244 #if defined(__MINGW32__)
1245     MingDIR         *dir;
1246     WIN32_FIND_DATA *dirbuf;
1247 #elif HAVE_OPENDIR
1248     DIR_enum_state  *dir;
1249     DIR_enum_item   *dirbuf;
1250 #endif
1251 #if defined(__MINGW32__)
1252     DWORD           fileAttributes;
1253 #else
1254     struct stat     statbuf;
1255 #endif
1256     const char      *cpath;
1257     
1258     RELEASE(self->currentFileName); self->currentFileName = nil;
1259     RELEASE(self->currentFilePath); self->currentFilePath = nil;
1260     
1261     while ([self->pathStack count]) {
1262 #if defined(__MINGW32__)
1263         dir = (MingDIR *)[[self->enumStack lastObject] pointerValue];
1264         dirbuf = ming_readdir(dir);
1265
1266         if (dirbuf == NULL) {
1267             // If we reached the end of this directory, go back to the upper one
1268             [self backtrack];
1269             continue;
1270         }
1271         /* Skip "." and ".." directory entries */
1272         if (Strcmp(dirbuf->cFileName, ".") == 0 || 
1273             Strcmp(dirbuf->cFileName, "..") == 0)
1274                 continue;
1275
1276         self->currentFileName = [manager
1277                 stringWithFileSystemRepresentation:dirbuf->cFileName
1278                 length:Strlen(dirbuf->cFileName)];
1279 #elif HAVE_OPENDIR
1280         dir    = (DIR*)[[enumStack lastObject] pointerValue];
1281         dirbuf = readdir(dir);
1282
1283         /* If we reached the end of this directory, go back to the upper one */
1284         if (dirbuf == NULL) {
1285             [self backtrack];
1286             continue;
1287         }
1288
1289         /* Skip "." and ".." directory entries */
1290         if (Strcmp(dirbuf->d_name, ".") == 0 || 
1291             Strcmp(dirbuf->d_name, "..") == 0)
1292                 continue;
1293         // Name of current file
1294         self->currentFileName = [manager
1295                 stringWithFileSystemRepresentation:dirbuf->d_name
1296                 length:Strlen(dirbuf->d_name)];
1297 #else
1298         [[InvalidArgumentException new] raise];
1299 #endif
1300         self->currentFileName
1301             = RETAIN([[pathStack lastObject]
1302                          stringByAppendingPathComponent:self->currentFileName]);
1303         
1304         // Full path of current file
1305         self->currentFilePath
1306             = RETAIN([self->topPath stringByAppendingPathComponent:
1307                                       self->currentFileName]);
1308         
1309         // Check if directory
1310         cpath = [manager fileSystemRepresentationWithPath:currentFilePath];
1311         
1312         // Do not follow links
1313 #ifdef S_IFLNK
1314         if (!flags.isFollowing) {
1315             if (lstat(cpath, &statbuf) < 0) {
1316                 NSLog (@"cannot lstat file '%s'", cpath);
1317                 continue;
1318             }
1319             // If link then return it as link
1320             if (S_IFLNK == (S_IFMT & statbuf.st_mode)) 
1321                 break;
1322         }
1323 #endif
1324
1325 #if defined(__MINGW32__)
1326         if ((fileAttributes = GetFileAttributes(cpath)) == 0xFFFFFFFF)
1327             // could not get file attributes
1328             continue;
1329
1330         if (self->flags.isRecursive) {
1331             if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1332                 [self recurseIntoDirectory:self->currentFilePath 
1333                       relativeName:self->currentFileName];
1334             }
1335         }
1336 #else
1337         // Follow links - check for directory
1338         if (stat(cpath, &statbuf) < 0)
1339             // could not stat file
1340             continue;
1341
1342         if (S_IFDIR == (S_IFMT & statbuf.st_mode) && self->flags.isRecursive) {
1343             [self recurseIntoDirectory:self->currentFilePath 
1344                   relativeName:self->currentFileName];
1345         }
1346 #endif
1347         break;
1348     }
1349 }
1350
1351 // Initializing
1352
1353 - (id)initWithDirectoryPath:(NSString*)path 
1354   recurseIntoSubdirectories:(BOOL)recurse
1355   followSymlinks:(BOOL)follow
1356   prefixFiles:(BOOL)prefix
1357 {
1358     self->pathStack = [[NSMutableArray allocWithZone:[self zone]] init];
1359     self->enumStack = [[NSMutableArray allocWithZone:[self zone]] init];
1360     self->flags.isRecursive = recurse;
1361     self->flags.isFollowing = follow;
1362     
1363     self->topPath = [path copyWithZone:[self zone]];
1364     [self recurseIntoDirectory:path relativeName:@""];
1365     
1366     return self;
1367 }
1368
1369 - (void)dealloc
1370 {
1371     while ([self->pathStack count])
1372         [self backtrack];
1373     
1374     RELEASE(self->pathStack);
1375     RELEASE(self->enumStack);
1376     RELEASE(self->currentFileName);
1377     RELEASE(self->currentFilePath);
1378     RELEASE(self->topPath);
1379
1380     [super dealloc];
1381 }
1382
1383 // Getting attributes
1384
1385 - (NSDictionary *)directoryAttributes
1386 {
1387     return [[NSFileManager defaultManager]
1388                            fileAttributesAtPath:self->currentFilePath
1389                            traverseLink:self->flags.isFollowing];
1390 }
1391
1392 - (NSDictionary*)fileAttributes
1393 {
1394     return [[NSFileManager defaultManager]
1395                            fileAttributesAtPath:self->currentFilePath
1396                            traverseLink:self->flags.isFollowing];
1397 }
1398
1399 // Skipping subdirectories
1400
1401 - (void)skipDescendents
1402 {
1403     if ([self->pathStack count])
1404         [self backtrack];
1405 }
1406
1407 // Enumerate next
1408
1409 - (id)nextObject
1410 {
1411     [self findNextFile];
1412     return self->currentFileName;
1413 }
1414
1415 @end /* NSDirectoryEnumerator */
1416
1417 /*
1418  * Attributes dictionary access
1419  */
1420
1421 @implementation NSDictionary(NSFileAttributes)
1422
1423 - (NSNumber *)fileSize
1424 {
1425     return [self objectForKey:NSFileSize];
1426 }
1427 - (NSString *)fileType;
1428 {
1429     return [self objectForKey:NSFileType];
1430 }
1431 - (NSNumber *)fileOwnerAccountNumber;
1432 {
1433     return [self objectForKey:NSFileOwnerAccountNumber];
1434 }
1435 - (NSNumber *)fileGroupOwnerAccountNumber;
1436 {
1437     return [self objectForKey:NSFileGroupOwnerAccountNumber];
1438 }
1439 - (NSDate *)fileModificationDate;
1440 {
1441     return [self objectForKey:NSFileModificationDate];
1442 }
1443 - (NSNumber *)filePosixPermissions;
1444 {
1445     return [self objectForKey:NSFilePosixPermissions];
1446 }
1447 @end
1448
1449 /*
1450  * File attributes names
1451  */
1452
1453 /* File Attributes */
1454
1455 LF_DECLARE NSString *NSFileSize                    = @"NSFileSize";
1456 LF_DECLARE NSString *NSFileModificationDate        = @"NSFileModificationDate";
1457 LF_DECLARE NSString *NSFileOwnerAccountNumber      = @"NSFileOwnerAccountNumber";
1458 LF_DECLARE NSString *NSFileOwnerAccountName        = @"NSFileOwnerAccountName";
1459 LF_DECLARE NSString *NSFileGroupOwnerAccountNumber = @"NSFileGroupOwnerAccountNumber";
1460 LF_DECLARE NSString *NSFileGroupOwnerAccountName   = @"NSFileGroupOwnerAccountName";
1461 LF_DECLARE NSString *NSFileReferenceCount          = @"NSFileReferenceCount";
1462 LF_DECLARE NSString *NSFileIdentifier              = @"NSFileIdentifier";
1463 LF_DECLARE NSString *NSFileDeviceIdentifier        = @"NSFileDeviceIdentifier";
1464 LF_DECLARE NSString *NSFilePosixPermissions        = @"NSFilePosixPermissions";
1465 LF_DECLARE NSString *NSFileType                    = @"NSFileType";
1466
1467 /* File Types */
1468
1469 LF_DECLARE NSString *NSFileTypeDirectory           = @"NSFileTypeDirectory";
1470 LF_DECLARE NSString *NSFileTypeRegular             = @"NSFileTypeRegular";
1471 LF_DECLARE NSString *NSFileTypeSymbolicLink        = @"NSFileTypeSymbolicLink";
1472 LF_DECLARE NSString *NSFileTypeSocket              = @"NSFileTypeSocket";
1473 LF_DECLARE NSString *NSFileTypeFifo                = @"NSFileTypeFifo";
1474 LF_DECLARE NSString *NSFileTypeCharacterSpecial    = @"NSFileTypeCharacterSpecial";
1475 LF_DECLARE NSString *NSFileTypeBlockSpecial        = @"NSFileTypeBlockSpecial";
1476 LF_DECLARE NSString *NSFileTypeUnknown             = @"NSFileTypeUnknown";
1477
1478 /* FileSystem Attributes */
1479
1480 LF_DECLARE NSString *NSFileSystemFileNumber        = @"NSFileSystemFileNumber";
1481 LF_DECLARE NSString *NSFileSystemSize              = @"NSFileSystemSize";
1482 LF_DECLARE NSString *NSFileSystemFreeSize          = @"NSFileSystemFreeSize";
1483 LF_DECLARE NSString *NSFileSystemNodes             = @"NSFileSystemNodes";
1484 LF_DECLARE NSString *NSFileSystemFreeNodes         = @"NSFileSystemFreeNodes";
1485 LF_DECLARE NSString *NSFileSystemNumber            = @"NSFileSystemNumber";
1486
1487 @implementation NSFileManager (PrivateMethods)
1488
1489 - (BOOL)_copyFile:(NSString*)source toFile:(NSString*)destination
1490   handler:handler
1491 {
1492     NSDictionary* attributes;
1493     int i, bufsize = 8096;
1494     int sourceFd, destFd, fileSize, fileMode;
1495     int rbytes, wbytes;
1496     char buffer[bufsize];
1497
1498     /* Assumes source is a file and exists! */
1499     NSAssert1 ([self fileExistsAtPath:source],
1500                 @"source file '%@' does not exist!", source);
1501
1502     attributes = [self fileAttributesAtPath:source traverseLink:NO];
1503     NSAssert1 (attributes, @"could not get the attributes for file '%@'",
1504                 source);
1505
1506     fileSize = [[attributes objectForKey:NSFileSize] intValue];
1507     fileMode = [[attributes objectForKey:NSFilePosixPermissions] intValue];
1508
1509     /* Open the source file. In case of error call the handler. */
1510     sourceFd = open([self fileSystemRepresentationWithPath:source], O_RDONLY, 0);
1511     if (sourceFd < 0) {
1512         if (handler) {
1513             NSDictionary* errorInfo
1514                 = [NSDictionary dictionaryWithObjectsAndKeys:
1515                         source, @"Path",
1516                         @"cannot open file for reading", @"Error",
1517                         nil];
1518             return [handler fileManager:self
1519                             shouldProceedAfterError:errorInfo];
1520         }
1521         else
1522             return NO;
1523     }
1524
1525     /* Open the destination file. In case of error call the handler. */
1526     destFd = open([self fileSystemRepresentationWithPath:destination],
1527                   O_WRONLY|O_CREAT|O_TRUNC, fileMode);
1528     if (destFd < 0) {
1529         if (handler) {
1530             NSDictionary* errorInfo
1531                 = [NSDictionary dictionaryWithObjectsAndKeys:
1532                         destination, @"ToPath",
1533                         @"cannot open file for writing", @"Error",
1534                         nil];
1535             close (sourceFd);
1536             return [handler fileManager:self
1537                             shouldProceedAfterError:errorInfo];
1538         }
1539         else
1540             return NO;
1541     }
1542
1543     /* Read bufsize bytes from source file and write them into the destination
1544        file. In case of errors call the handler and abort the operation. */
1545     for (i = 0; i < fileSize; i += rbytes) {
1546         rbytes = read (sourceFd, buffer, bufsize);
1547         if (rbytes < 0) {
1548             if (handler) {
1549                 NSDictionary* errorInfo
1550                     = [NSDictionary dictionaryWithObjectsAndKeys:
1551                             source, @"Path",
1552                             @"cannot read from file", @"Error",
1553                             nil];
1554                 close (sourceFd);
1555                 close (destFd);
1556                 return [handler fileManager:self
1557                                 shouldProceedAfterError:errorInfo];
1558             }
1559             else
1560                 return NO;
1561         }
1562
1563         wbytes = write (destFd, buffer, rbytes);
1564         if (wbytes != rbytes) {
1565             if (handler) {
1566                 NSDictionary* errorInfo
1567                     = [NSDictionary dictionaryWithObjectsAndKeys:
1568                             source, @"Path",
1569                             destination, @"ToPath",
1570                             @"cannot write to file", @"Error",
1571                             nil];
1572                 close (sourceFd);
1573                 close (destFd);
1574                 return [handler fileManager:self
1575                                 shouldProceedAfterError:errorInfo];
1576             }
1577             else
1578                 return NO;
1579         }
1580     }
1581     close (sourceFd);
1582     close (destFd);
1583
1584     return YES;
1585 }
1586
1587 - (BOOL)_copyPath:(NSString*)source
1588   toPath:(NSString*)destination
1589   handler:handler
1590 {
1591     NSDirectoryEnumerator* enumerator;
1592     NSString* dirEntry;
1593     NSString* sourceFile;
1594     NSString* fileType;
1595     NSString* destinationFile;
1596     NSDictionary* attributes;
1597     CREATE_AUTORELEASE_POOL(pool);
1598
1599     enumerator = [self enumeratorAtPath:source];
1600     while ((dirEntry = [enumerator nextObject])) {
1601         attributes = [enumerator fileAttributes];
1602         fileType = [attributes objectForKey:NSFileType];
1603         sourceFile = [source stringByAppendingPathComponent:dirEntry];
1604         destinationFile
1605                 = [destination stringByAppendingPathComponent:dirEntry];
1606
1607         [handler fileManager:self willProcessPath:sourceFile];
1608         if ([fileType isEqual:NSFileTypeDirectory]) {
1609             if (![self createDirectoryAtPath:destinationFile
1610                         attributes:attributes]) {
1611                 if (handler) {
1612                     NSDictionary* errorInfo
1613                         = [NSDictionary dictionaryWithObjectsAndKeys:
1614                                 destinationFile, @"Path",
1615                                 @"cannot create directory", @"Error",
1616                                 nil];
1617                     if (![handler fileManager:self
1618                                   shouldProceedAfterError:errorInfo])
1619                         return NO;
1620                 }
1621                 else
1622                     return NO;
1623             }
1624             else {
1625                 [enumerator skipDescendents];
1626                 if (![self _copyPath:sourceFile toPath:destinationFile
1627                             handler:handler])
1628                     return NO;
1629             }
1630         }
1631         else if ([fileType isEqual:NSFileTypeRegular]) {
1632             if (![self _copyFile:sourceFile toFile:destinationFile
1633                         handler:handler])
1634                 return NO;
1635         }
1636         else if ([fileType isEqual:NSFileTypeSymbolicLink]) {
1637             if (![self createSymbolicLinkAtPath:destinationFile
1638                         pathContent:sourceFile]) {
1639                 if (handler) {
1640                     NSDictionary* errorInfo
1641                         = [NSDictionary dictionaryWithObjectsAndKeys:
1642                                 sourceFile, @"Path",
1643                                 destinationFile, @"ToPath",
1644                                 @"cannot create symbolic link", @"Error",
1645                                 nil];
1646                     if (![handler fileManager:self
1647                                   shouldProceedAfterError:errorInfo])
1648                         return NO;
1649                 }
1650                 else
1651                     return NO;
1652             }
1653         }
1654         else {
1655             NSLog(@"cannot copy file '%@' of type '%@'", sourceFile, fileType);
1656         }
1657         [self changeFileAttributes:attributes atPath:destinationFile];
1658     }
1659     RELEASE(pool);
1660
1661     return YES;
1662 }
1663
1664 @end /* NSFileManager (PrivateMethods) */
1665 /*
1666   Local Variables:
1667   c-basic-offset: 4
1668   tab-width: 8
1669   End:
1670 */
1671