1997-03-03 20:10:10 +00:00
|
|
|
/*
|
|
|
|
NSFileManager.m
|
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
Copyright (C) 1997-1999 Free Software Foundation, Inc.
|
1997-03-03 20:10:10 +00:00
|
|
|
|
|
|
|
Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
|
|
|
|
Author: Ovidiu Predescu <ovidiu@net-community.com>
|
|
|
|
Date: Feb 1997
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free
|
|
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
1997-11-06 00:51:23 +00:00
|
|
|
#include <config.h>
|
1998-12-20 21:27:47 +00:00
|
|
|
#include <base/preface.h>
|
1997-03-03 20:10:10 +00:00
|
|
|
#include <Foundation/NSFileManager.h>
|
1997-09-13 17:52:31 +00:00
|
|
|
#include <Foundation/NSException.h>
|
1997-11-03 01:40:03 +00:00
|
|
|
#include <Foundation/NSAutoreleasePool.h>
|
1997-09-13 17:52:31 +00:00
|
|
|
#include <Foundation/NSLock.h>
|
1999-02-21 20:01:41 +00:00
|
|
|
#include <Foundation/NSDebug.h>
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1998-02-05 22:06:20 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
/* determine directory reading files */
|
|
|
|
|
|
|
|
#if defined(HAVE_DIRENT_H)
|
|
|
|
# include <dirent.h>
|
|
|
|
#elif defined(HAVE_SYS_DIR_H)
|
|
|
|
# include <sys/dir.h>
|
|
|
|
#elif defined(HAVE_SYS_NDIR_H)
|
|
|
|
# include <sys/ndir.h>
|
|
|
|
#elif defined(HAVE_NDIR_H)
|
|
|
|
# include <ndir.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#if !defined(_POSIX_VERSION)
|
|
|
|
# if defined(NeXT)
|
|
|
|
# define DIR_enum_item struct direct
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(DIR_enum_item)
|
|
|
|
# define DIR_enum_item struct dirent
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define DIR_enum_state DIR
|
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
#define WIN32ERR ((DWORD)0xFFFFFFFF)
|
|
|
|
#endif
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
/* determine filesystem max path length */
|
|
|
|
|
|
|
|
#ifdef _POSIX_VERSION
|
|
|
|
# include <limits.h> /* for PATH_MAX */
|
|
|
|
# include <utime.h>
|
|
|
|
#else
|
1999-05-11 09:21:38 +00:00
|
|
|
#ifdef __WIN32__
|
1999-05-05 18:45:52 +00:00
|
|
|
# include <limits.h>
|
|
|
|
# include <sys/utime.h>
|
|
|
|
#else
|
1997-03-03 20:10:10 +00:00
|
|
|
# include <sys/param.h> /* for MAXPATHLEN */
|
|
|
|
#endif
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif
|
1997-03-03 20:10:10 +00:00
|
|
|
|
|
|
|
#ifndef PATH_MAX
|
|
|
|
# ifdef _POSIX_VERSION
|
|
|
|
# define PATH_MAX _POSIX_PATH_MAX
|
|
|
|
# else
|
|
|
|
# ifdef MAXPATHLEN
|
|
|
|
# define PATH_MAX MAXPATHLEN
|
|
|
|
# else
|
|
|
|
# define PATH_MAX 1024
|
|
|
|
# endif
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* determine if we have statfs struct and function */
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_VFS_H
|
|
|
|
# include <sys/vfs.h>
|
1998-02-14 23:36:06 +00:00
|
|
|
# ifdef HAVE_SYS_STATVFS_H
|
|
|
|
# include <sys/statvfs.h>
|
|
|
|
# endif
|
1997-03-03 20:10:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_STATFS_H
|
|
|
|
# include <sys/statfs.h>
|
|
|
|
#endif
|
|
|
|
|
1998-02-05 22:06:20 +00:00
|
|
|
#if HAVE_SYS_FILE_H
|
|
|
|
#include <sys/file.h>
|
|
|
|
#endif
|
|
|
|
|
1999-05-06 13:58:19 +00:00
|
|
|
#if HAVE_SYS_MOUNT_H
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#endif
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
#include <errno.h>
|
1997-09-13 17:52:31 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
1997-03-03 20:10:10 +00:00
|
|
|
#include <sys/stat.h>
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1998-02-05 22:06:20 +00:00
|
|
|
#include <fcntl.h>
|
1998-04-20 14:13:19 +00:00
|
|
|
#if HAVE_PWD_H
|
|
|
|
#include <pwd.h> /* For struct passwd */
|
|
|
|
#endif
|
1998-12-03 16:49:25 +00:00
|
|
|
#if HAVE_GRP_H
|
|
|
|
#include <grp.h> /* For struct group */
|
|
|
|
#endif
|
1998-02-05 22:06:20 +00:00
|
|
|
#if HAVE_UTIME_H
|
|
|
|
# include <utime.h>
|
|
|
|
#endif
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
/* include usual headers */
|
|
|
|
|
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
#include <Foundation/NSDictionary.h>
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <Foundation/NSValue.h>
|
|
|
|
#include <Foundation/NSPathUtilities.h>
|
|
|
|
#include <Foundation/NSFileManager.h>
|
|
|
|
|
1998-02-05 22:06:20 +00:00
|
|
|
@interface NSFileManager (PrivateMethods)
|
|
|
|
|
|
|
|
/* Copies the contents of source file to destination file. Assumes source
|
|
|
|
and destination are regular files or symbolic links. */
|
1999-06-18 10:28:11 +00:00
|
|
|
- (BOOL) _copyFile: (NSString*)source
|
|
|
|
toFile: (NSString*)destination
|
|
|
|
handler: (id)handler;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
|
|
|
/* Recursively copies the contents of source directory to destination. */
|
1999-06-18 10:28:11 +00:00
|
|
|
- (BOOL) _copyPath: (NSString*)source
|
|
|
|
toPath: (NSString*)destination
|
|
|
|
handler: (id)handler;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (NSDictionary*) _attributesAtPath: (NSString*)path
|
|
|
|
traverseLink: (BOOL)traverse
|
|
|
|
forCopy: (BOOL)copy;
|
1998-02-05 22:06:20 +00:00
|
|
|
@end /* NSFileManager (PrivateMethods) */
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
@interface NSDirectoryEnumerator (PrivateMethods)
|
|
|
|
- (NSDictionary*) _attributesForCopy;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
/*
|
|
|
|
* NSFileManager implementation
|
|
|
|
*/
|
|
|
|
|
|
|
|
@implementation NSFileManager
|
|
|
|
|
|
|
|
// Getting the default manager
|
|
|
|
|
|
|
|
static NSFileManager* defaultManager = nil;
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
+ (NSFileManager*) defaultManager
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1997-09-13 17:52:31 +00:00
|
|
|
if (!defaultManager)
|
|
|
|
{
|
|
|
|
NS_DURING
|
|
|
|
{
|
|
|
|
[gnustep_global_lock lock];
|
|
|
|
defaultManager = [[self alloc] init];
|
|
|
|
[gnustep_global_lock unlock];
|
|
|
|
}
|
|
|
|
NS_HANDLER
|
|
|
|
{
|
|
|
|
// unlock then re-raise the exception
|
|
|
|
[gnustep_global_lock unlock];
|
|
|
|
[localException raise];
|
|
|
|
}
|
|
|
|
NS_ENDHANDLER
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1997-09-13 17:52:31 +00:00
|
|
|
return defaultManager;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
TEST_RELEASE(lastError);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
// Directory operations
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) changeCurrentDirectoryPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1997-09-13 17:52:31 +00:00
|
|
|
return SetCurrentDirectory(cpath);
|
|
|
|
#else
|
1997-03-03 20:10:10 +00:00
|
|
|
return (chdir(cpath) == 0);
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) createDirectoryAtPath: (NSString*)path
|
|
|
|
attributes: (NSDictionary*)attributes
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL ok;
|
|
|
|
|
|
|
|
ok = CreateDirectory([self fileSystemRepresentationWithPath: path], NULL);
|
|
|
|
if (ok == NO)
|
|
|
|
ASSIGN(lastError, @"Could not create directory");
|
|
|
|
return ok;
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1999-06-17 19:09:25 +00:00
|
|
|
const char *cpath;
|
|
|
|
char dirpath[PATH_MAX+1];
|
|
|
|
struct stat statbuf;
|
|
|
|
int len, cur;
|
|
|
|
NSDictionary *needChown = nil;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
/*
|
|
|
|
* If there is no file owner specified, and we are running setuid to
|
|
|
|
* root, then we assume we need to change ownership to correct user.
|
|
|
|
*/
|
|
|
|
if ([attributes objectForKey: NSFileOwnerAccountName] == nil
|
|
|
|
&& [attributes objectForKey: NSFileOwnerAccountNumber] == nil
|
|
|
|
&& geteuid() == 0 && [@"root" isEqualToString: NSUserName()] == NO)
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
needChown = [NSDictionary dictionaryWithObjectsAndKeys:
|
1999-02-20 21:19:15 +00:00
|
|
|
NSFileOwnerAccountName, NSUserName(), nil];
|
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
cpath = [self fileSystemRepresentationWithPath: path];
|
1999-02-20 21:19:15 +00:00
|
|
|
len = strlen(cpath);
|
|
|
|
if (len > PATH_MAX) // name too long
|
1999-06-18 10:28:11 +00:00
|
|
|
{
|
|
|
|
ASSIGN(lastError, @"Could not create directory - name too long");
|
|
|
|
return NO;
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
if (strcmp(cpath, "/") == 0 || len == 0) // cannot use "/" or ""
|
1999-06-18 10:28:11 +00:00
|
|
|
{
|
|
|
|
ASSIGN(lastError, @"Could not create directory - no name given");
|
|
|
|
return NO;
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
strcpy(dirpath, cpath);
|
|
|
|
dirpath[len] = '\0';
|
|
|
|
if (dirpath[len-1] == '/')
|
|
|
|
dirpath[len-1] = '\0';
|
|
|
|
cur = 0;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
// find next '/'
|
|
|
|
while (dirpath[cur] != '/' && cur < len)
|
|
|
|
cur++;
|
|
|
|
// if first char is '/' then again; (cur == len) -> last component
|
|
|
|
if (cur == 0)
|
|
|
|
{
|
|
|
|
cur++;
|
|
|
|
continue;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
// check if path from 0 to cur is valid
|
|
|
|
dirpath[cur] = '\0';
|
|
|
|
if (stat(dirpath, &statbuf) == 0)
|
|
|
|
{
|
|
|
|
if (cur == len)
|
1999-06-18 10:28:11 +00:00
|
|
|
{
|
|
|
|
ASSIGN(lastError, @"Could not create directory - already exists");
|
|
|
|
return NO;
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// make new directory
|
|
|
|
if (mkdir(dirpath, 0777) != 0)
|
1999-06-18 10:28:11 +00:00
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = [NSString stringWithFormat: @"Could not create '%s' - '%s'",
|
|
|
|
dirpath, strerror(errno)];
|
|
|
|
ASSIGN(lastError, s);
|
|
|
|
return NO;
|
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
// if last directory and attributes then change
|
|
|
|
if (cur == len && attributes)
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
if ([self changeFileAttributes: attributes
|
|
|
|
atPath: [self stringWithFileSystemRepresentation: dirpath
|
|
|
|
length: cur]] == NO)
|
1999-02-20 21:19:15 +00:00
|
|
|
return NO;
|
|
|
|
if (needChown)
|
|
|
|
{
|
|
|
|
if ([self changeFileAttributes: needChown
|
1999-05-08 05:43:48 +00:00
|
|
|
atPath: [self stringWithFileSystemRepresentation: dirpath
|
|
|
|
length: cur]] == NO)
|
1999-02-20 21:19:15 +00:00
|
|
|
{
|
|
|
|
NSLog(@"Failed to change ownership of '%s' to '%@'",
|
|
|
|
dirpath, NSUserName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
dirpath[cur] = '/';
|
|
|
|
cur++;
|
|
|
|
}
|
|
|
|
while (cur < len);
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
return YES;
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif /* WIN32 */
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSString*) currentDirectoryPath
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
char path[PATH_MAX];
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-06-18 10:28:11 +00:00
|
|
|
if (GetCurrentDirectory(PATH_MAX, path) > PATH_MAX)
|
|
|
|
return nil;
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1997-03-03 20:10:10 +00:00
|
|
|
#ifdef HAVE_GETCWD
|
1999-06-18 10:28:11 +00:00
|
|
|
if (getcwd(path, PATH_MAX-1) == NULL)
|
|
|
|
return nil;
|
1997-03-03 20:10:10 +00:00
|
|
|
#else
|
1999-06-18 10:28:11 +00:00
|
|
|
if (getwd(path) == NULL)
|
|
|
|
return nil;
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif /* HAVE_GETCWD */
|
|
|
|
#endif /* WIN32 */
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
return [self stringWithFileSystemRepresentation: path length: strlen(path)];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// File operations
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (BOOL) copyPath: (NSString*)source
|
|
|
|
toPath: (NSString*)destination
|
|
|
|
handler: handler
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL sourceIsDir;
|
|
|
|
BOOL fileExists;
|
|
|
|
NSDictionary *attributes;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
fileExists = [self fileExistsAtPath: source isDirectory: &sourceIsDir];
|
|
|
|
if (!fileExists)
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
fileExists = [self fileExistsAtPath: destination];
|
|
|
|
if (fileExists)
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
attributes = [self _attributesAtPath: source traverseLink: NO forCopy: YES];
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
if (sourceIsDir)
|
|
|
|
{
|
|
|
|
/* If destination directory is a descendant of source directory copying
|
|
|
|
isn't possible. */
|
|
|
|
if ([[destination stringByAppendingString: @"/"]
|
|
|
|
hasPrefix: [source stringByAppendingString: @"/"]])
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
[handler fileManager: self willProcessPath: destination];
|
|
|
|
if (![self createDirectoryAtPath: destination attributes: attributes])
|
|
|
|
{
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
destination, @"Path",
|
|
|
|
lastError, @"Error",
|
|
|
|
nil];
|
|
|
|
return [handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo];
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
else
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
if (sourceIsDir)
|
|
|
|
{
|
|
|
|
if (![self _copyPath: source toPath: destination handler: handler])
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self changeFileAttributes: attributes atPath: destination];
|
|
|
|
return YES;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
[handler fileManager: self willProcessPath: source];
|
|
|
|
if (![self _copyFile: source toFile: destination handler: handler])
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self changeFileAttributes: attributes atPath: destination];
|
|
|
|
return YES;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
return NO;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (BOOL) movePath: (NSString*)source
|
|
|
|
toPath: (NSString*)destination
|
|
|
|
handler: handler
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL sourceIsDir, fileExists;
|
|
|
|
const char* sourcePath = [self fileSystemRepresentationWithPath: source];
|
|
|
|
const char* destPath = [self fileSystemRepresentationWithPath: destination];
|
|
|
|
NSString* destinationParent;
|
|
|
|
unsigned int sourceDevice, destinationDevice;
|
|
|
|
|
|
|
|
fileExists = [self fileExistsAtPath: source isDirectory: &sourceIsDir];
|
|
|
|
if (!fileExists)
|
|
|
|
return NO;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
fileExists = [self fileExistsAtPath: destination];
|
|
|
|
if (fileExists)
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
/* Check to see if the source and destination's parent are on the same
|
|
|
|
physical device so we can perform a rename syscall directly. */
|
|
|
|
sourceDevice = [[[self fileSystemAttributesAtPath: source]
|
|
|
|
objectForKey: NSFileSystemNumber]
|
|
|
|
unsignedIntValue];
|
|
|
|
destinationParent = [destination stringByDeletingLastPathComponent];
|
|
|
|
if ([destinationParent isEqual: @""])
|
|
|
|
destinationParent = @".";
|
|
|
|
destinationDevice
|
|
|
|
= [[[self fileSystemAttributesAtPath: destinationParent]
|
|
|
|
objectForKey: NSFileSystemNumber]
|
|
|
|
unsignedIntValue];
|
|
|
|
|
|
|
|
if (sourceDevice != destinationDevice)
|
|
|
|
{
|
|
|
|
/* If destination directory is a descendant of source directory moving
|
|
|
|
isn't possible. */
|
|
|
|
if (sourceIsDir && [[destination stringByAppendingString: @"/"]
|
|
|
|
hasPrefix: [source stringByAppendingString: @"/"]])
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
if ([self copyPath: source toPath: destination handler: handler])
|
|
|
|
{
|
|
|
|
NSDictionary* attributes;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
attributes = [self _attributesAtPath: source
|
|
|
|
traverseLink: NO
|
|
|
|
forCopy: YES];
|
|
|
|
[self changeFileAttributes: attributes atPath: destination];
|
|
|
|
return [self removeFileAtPath: source handler: handler];
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
else
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* source and destination are on the same device so we can simply
|
|
|
|
invoke rename on source. */
|
|
|
|
[handler fileManager: self willProcessPath: source];
|
|
|
|
if (rename (sourcePath, destPath) == -1)
|
|
|
|
{
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
source, @"Path",
|
|
|
|
destination, @"ToPath",
|
|
|
|
@"cannot move file", @"Error",
|
|
|
|
nil];
|
|
|
|
if ([handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo])
|
|
|
|
return YES;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
return NO;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
return YES;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
return NO;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (BOOL) linkPath: (NSString*)source
|
|
|
|
toPath: (NSString*)destination
|
|
|
|
handler: handler
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
|
|
|
// TODO
|
1999-05-08 05:43:48 +00:00
|
|
|
[self notImplemented: _cmd];
|
1997-03-03 20:10:10 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) removeFileAtPath: (NSString*)path
|
1999-06-18 10:28:11 +00:00
|
|
|
handler: handler
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1997-11-03 01:40:03 +00:00
|
|
|
NSArray *contents;
|
|
|
|
|
|
|
|
if (handler)
|
|
|
|
[handler fileManager: self willProcessPath: path];
|
|
|
|
|
|
|
|
contents = [self directoryContentsAtPath: path];
|
|
|
|
if (contents == nil)
|
|
|
|
{
|
|
|
|
if (unlink([path fileSystemRepresentation]) < 0)
|
|
|
|
{
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSMutableDictionary *info;
|
|
|
|
|
|
|
|
info = [[NSMutableDictionary alloc] initWithCapacity: 3];
|
|
|
|
[info setObject: path forKey: @"Path"];
|
|
|
|
[info setObject: [NSString stringWithCString: strerror(errno)]
|
|
|
|
forKey: @"Error"];
|
|
|
|
result = [handler fileManager: self
|
|
|
|
shouldProceedAfterError: info];
|
1999-02-21 20:01:41 +00:00
|
|
|
RELEASE(info);
|
1997-11-03 01:40:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NO;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
contents = [self directoryContentsAtPath: path];
|
|
|
|
for (i = 0; i < [contents count]; i++)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
1997-11-03 01:40:03 +00:00
|
|
|
NSString *item;
|
|
|
|
NSString *next;
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
item = [contents objectAtIndex: i];
|
|
|
|
next = [path stringByAppendingPathComponent: item];
|
|
|
|
result = [self removeFileAtPath: next handler: handler];
|
1999-06-18 10:28:11 +00:00
|
|
|
RELEASE(arp);
|
1997-11-03 01:40:03 +00:00
|
|
|
if (result == NO)
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rmdir([path fileSystemRepresentation]) < 0)
|
|
|
|
{
|
|
|
|
BOOL result;
|
|
|
|
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSMutableDictionary *info;
|
|
|
|
|
|
|
|
info = [[NSMutableDictionary alloc] initWithCapacity: 3];
|
|
|
|
[info setObject: path forKey: @"Path"];
|
|
|
|
[info setObject: [NSString stringWithCString: strerror(errno)]
|
|
|
|
forKey: @"Error"];
|
|
|
|
result = [handler fileManager: self
|
|
|
|
shouldProceedAfterError: info];
|
1999-02-21 20:01:41 +00:00
|
|
|
RELEASE(info);
|
1997-11-03 01:40:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
result = NO;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return YES;
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
- (BOOL) createFileAtPath: (NSString*)path
|
|
|
|
contents: (NSData*)contents
|
|
|
|
attributes: (NSDictionary*)attributes
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-20 21:19:15 +00:00
|
|
|
int fd, len, written;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
fd = open ([self fileSystemRepresentationWithPath: path],
|
1998-02-05 22:06:20 +00:00
|
|
|
O_WRONLY|O_TRUNC|O_CREAT, 0644);
|
1999-02-20 21:19:15 +00:00
|
|
|
if (fd < 0)
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
if (![self changeFileAttributes: attributes atPath: path])
|
|
|
|
{
|
|
|
|
close (fd);
|
|
|
|
return NO;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
/*
|
|
|
|
* If there is no file owner specified, and we are running setuid to
|
|
|
|
* root, then we assume we need to change ownership to correct user.
|
|
|
|
*/
|
|
|
|
if ([attributes objectForKey: NSFileOwnerAccountName] == nil
|
|
|
|
&& [attributes objectForKey: NSFileOwnerAccountNumber] == nil
|
|
|
|
&& geteuid() == 0 && [@"root" isEqualToString: NSUserName()] == NO)
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
1999-02-20 21:19:15 +00:00
|
|
|
NSFileOwnerAccountName, NSUserName(), nil];
|
|
|
|
if (![self changeFileAttributes: attributes atPath: path])
|
|
|
|
{
|
|
|
|
NSLog(@"Failed to change ownership of '%@' to '%@'",
|
|
|
|
path, NSUserName());
|
|
|
|
}
|
|
|
|
}
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
len = [contents length];
|
|
|
|
if (len)
|
|
|
|
written = write (fd, [contents bytes], len);
|
|
|
|
else
|
|
|
|
written = 0;
|
|
|
|
close (fd);
|
|
|
|
|
|
|
|
return written == len;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Getting and comparing file contents
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSData*) contentsAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
return [NSData dataWithContentsOfFile: path];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) contentsEqualAtPath: (NSString*)path1 andPath: (NSString*)path2
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
NSDictionary *d1;
|
|
|
|
NSDictionary *d2;
|
|
|
|
NSString *t;
|
|
|
|
|
|
|
|
if ([path1 isEqual: path2])
|
|
|
|
return YES;
|
|
|
|
d1 = [self fileAttributesAtPath: path1 traverseLink: NO];
|
|
|
|
d2 = [self fileAttributesAtPath: path2 traverseLink: NO];
|
|
|
|
t = [d1 objectForKey: NSFileType];
|
|
|
|
if ([t isEqual: [d2 objectForKey: NSFileType]] == NO)
|
1997-03-03 20:10:10 +00:00
|
|
|
return NO;
|
1999-05-08 05:43:48 +00:00
|
|
|
if ([[d1 objectForKey: NSFileType] isEqual: NSFileTypeRegular])
|
|
|
|
{
|
|
|
|
id s1 = [d1 objectForKey: NSFileSize];
|
|
|
|
id s2 = [d2 objectForKey: NSFileSize];
|
|
|
|
|
|
|
|
if ([s1 isEqual: s2] == YES)
|
|
|
|
{
|
|
|
|
NSData *c1 = [NSData dataWithContentsOfFile: path1];
|
|
|
|
NSData *c2 = [NSData dataWithContentsOfFile: path2];
|
|
|
|
|
|
|
|
if ([c1 isEqual: c2])
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else if ([[d1 objectForKey: NSFileType] isEqual: NSFileTypeDirectory])
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
[self notImplemented: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return YES;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Detemining access to files
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) fileExistsAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
return [self fileExistsAtPath: path isDirectory: NULL];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) fileExistsAtPath: (NSString*)path isDirectory: (BOOL*)isDirectory
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
if (cpath == 0 || *cpath == '\0')
|
|
|
|
return NO;
|
|
|
|
else
|
1997-09-13 17:52:31 +00:00
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
DWORD res;
|
|
|
|
|
|
|
|
res = GetFileAttributes(cpath);
|
|
|
|
if (res == WIN32ERR)
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
if (isDirectory)
|
|
|
|
{
|
|
|
|
if (res & FILE_ATTRIBUTE_DIRECTORY)
|
|
|
|
*isDirectory = YES;
|
|
|
|
else
|
|
|
|
*isDirectory = NO;
|
|
|
|
}
|
|
|
|
return YES;
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1999-05-05 18:45:52 +00:00
|
|
|
struct stat statbuf;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
if (stat(cpath, &statbuf) != 0)
|
1997-03-03 20:10:10 +00:00
|
|
|
return NO;
|
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
if (isDirectory)
|
|
|
|
{
|
|
|
|
*isDirectory = ((statbuf.st_mode & S_IFMT) == S_IFDIR);
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
return YES;
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif /* WIN32 */
|
1999-05-05 18:45:52 +00:00
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) isReadableFileAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
|
|
|
|
if (cpath == 0 || *cpath == '\0')
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
DWORD res= GetFileAttributes(cpath);
|
|
|
|
|
|
|
|
if (res == WIN32ERR)
|
|
|
|
return NO;
|
|
|
|
return YES;
|
|
|
|
#else
|
|
|
|
return (access(cpath, R_OK) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) isWritableFileAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
|
|
|
|
if (cpath == 0 || *cpath == '\0')
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
DWORD res= GetFileAttributes(cpath);
|
|
|
|
|
|
|
|
if (res == WIN32ERR)
|
|
|
|
return NO;
|
|
|
|
return (res & FILE_ATTRIBUTE_READONLY) ? NO : YES;
|
|
|
|
#else
|
|
|
|
return (access(cpath, W_OK) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) isExecutableFileAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
|
|
|
|
if (cpath == 0 || *cpath == '\0')
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
DWORD res= GetFileAttributes(cpath);
|
|
|
|
int len = strlen(cpath);
|
|
|
|
|
|
|
|
if (res == WIN32ERR)
|
|
|
|
return NO;
|
|
|
|
if (len > 4 && strcmp(&path[len-4], ".exe") == 0)
|
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
#else
|
|
|
|
return (access(cpath, X_OK) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) isDeletableFileAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
if (path == nil)
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *cpath;
|
|
|
|
// TODO - handle directories
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
cpath = [self fileSystemRepresentationWithPath:
|
1997-03-03 20:10:10 +00:00
|
|
|
[path stringByDeletingLastPathComponent]];
|
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
if (access(cpath, X_OK || W_OK) != 0)
|
1997-03-03 20:10:10 +00:00
|
|
|
return NO;
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
cpath = [self fileSystemRepresentationWithPath:
|
1997-03-03 20:10:10 +00:00
|
|
|
[path stringByDeletingLastPathComponent]];
|
|
|
|
|
1999-05-05 18:45:52 +00:00
|
|
|
return (access(cpath, X_OK || W_OK) != 0);
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSDictionary*) fileAttributesAtPath: (NSString*)path traverseLink: (BOOL)flag
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
return [self _attributesAtPath: path traverseLink: flag forCopy: NO];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSDictionary*) fileSystemAttributesAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1997-09-13 17:52:31 +00:00
|
|
|
long long totalsize, freesize;
|
|
|
|
id values[5];
|
|
|
|
id keys[5] = {
|
|
|
|
NSFileSystemSize,
|
|
|
|
NSFileSystemFreeSize,
|
|
|
|
NSFileSystemNodes,
|
|
|
|
NSFileSystemFreeNodes,
|
|
|
|
NSFileSystemNumber
|
|
|
|
};
|
|
|
|
DWORD SectorsPerCluster, BytesPerSector, NumberFreeClusters;
|
|
|
|
DWORD TotalNumberClusters;
|
|
|
|
const char *cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
|
|
|
|
if (!GetDiskFreeSpace(cpath, &SectorsPerCluster,
|
|
|
|
&BytesPerSector, &NumberFreeClusters,
|
|
|
|
&TotalNumberClusters))
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
totalsize = TotalNumberClusters * SectorsPerCluster * BytesPerSector;
|
|
|
|
freesize = NumberFreeClusters * SectorsPerCluster * BytesPerSector;
|
|
|
|
|
|
|
|
values[0] = [NSNumber numberWithLongLong: totalsize];
|
|
|
|
values[1] = [NSNumber numberWithLongLong: freesize];
|
|
|
|
values[2] = [NSNumber numberWithLong: LONG_MAX];
|
|
|
|
values[3] = [NSNumber numberWithLong: LONG_MAX];
|
|
|
|
values[4] = [NSNumber numberWithUnsignedInt: 0];
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
return [NSDictionary dictionaryWithObjects: values forKeys: keys count: 5];
|
1997-09-13 17:52:31 +00:00
|
|
|
|
|
|
|
#else
|
1997-03-03 20:10:10 +00:00
|
|
|
#if HAVE_SYS_VFS_H || HAVE_SYS_STATFS_H
|
|
|
|
struct stat statbuf;
|
|
|
|
#if HAVE_STATVFS
|
|
|
|
struct statvfs statfsbuf;
|
|
|
|
#else
|
|
|
|
struct statfs statfsbuf;
|
|
|
|
#endif
|
|
|
|
long long totalsize, freesize;
|
1999-05-08 05:43:48 +00:00
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
|
|
|
id values[5];
|
|
|
|
id keys[5] = {
|
|
|
|
NSFileSystemSize,
|
|
|
|
NSFileSystemFreeSize,
|
|
|
|
NSFileSystemNodes,
|
|
|
|
NSFileSystemFreeNodes,
|
|
|
|
NSFileSystemNumber
|
|
|
|
};
|
|
|
|
|
|
|
|
if (stat(cpath, &statbuf) != 0)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
#if HAVE_STATVFS
|
|
|
|
if (statvfs(cpath, &statfsbuf) != 0)
|
|
|
|
return nil;
|
|
|
|
#else
|
|
|
|
if (statfs(cpath, &statfsbuf) != 0)
|
|
|
|
return nil;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
totalsize = statfsbuf.f_bsize * statfsbuf.f_blocks;
|
|
|
|
freesize = statfsbuf.f_bsize * statfsbuf.f_bfree;
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
values[0] = [NSNumber numberWithLongLong: totalsize];
|
|
|
|
values[1] = [NSNumber numberWithLongLong: freesize];
|
|
|
|
values[2] = [NSNumber numberWithLong: statfsbuf.f_files];
|
|
|
|
values[3] = [NSNumber numberWithLong: statfsbuf.f_ffree];
|
|
|
|
values[4] = [NSNumber numberWithUnsignedLong: statbuf.st_dev];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
return [NSDictionary dictionaryWithObjects: values forKeys: keys count: 5];
|
1997-03-03 20:10:10 +00:00
|
|
|
#else
|
|
|
|
return nil;
|
|
|
|
#endif
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif /* WIN32 */
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
- (BOOL) changeFileAttributes: (NSDictionary*)attributes atPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
const char *cpath = [self fileSystemRepresentationWithPath: path];
|
1999-02-20 21:19:15 +00:00
|
|
|
NSNumber *num;
|
|
|
|
NSString *str;
|
|
|
|
NSDate *date;
|
|
|
|
BOOL allOk = YES;
|
1997-09-13 17:52:31 +00:00
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
#ifndef __WIN32__
|
1999-02-20 21:19:15 +00:00
|
|
|
num = [attributes objectForKey: NSFileOwnerAccountNumber];
|
|
|
|
if (num)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
if (chown(cpath, [num intValue], -1) != 0)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFileOwnerAccountNumber to '%@'", num];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
else
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
if ((str = [attributes objectForKey: NSFileOwnerAccountName]) != nil)
|
1999-02-20 21:19:15 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL ok = NO;
|
1999-02-20 21:19:15 +00:00
|
|
|
#if HAVE_PWD_H
|
|
|
|
struct passwd *pw = getpwnam([str cString]);
|
|
|
|
|
|
|
|
if (pw)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
ok = (chown(cpath, pw->pw_uid, -1) == 0);
|
1999-02-20 21:19:15 +00:00
|
|
|
chown(cpath, -1, pw->pw_gid);
|
|
|
|
}
|
|
|
|
#endif
|
1999-06-18 10:28:11 +00:00
|
|
|
if (ok == NO)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFileOwnerAccountName to '%@'", str];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
num = [attributes objectForKey: NSFileGroupOwnerAccountNumber];
|
1999-02-20 21:19:15 +00:00
|
|
|
if (num)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
if (chown(cpath, -1, [num intValue]) != 0)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFileGroupOwnerAccountNumber to '%@'", num];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
1999-02-20 21:19:15 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
else if ((str=[attributes objectForKey: NSFileGroupOwnerAccountName]) != nil)
|
1999-02-20 21:19:15 +00:00
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL ok = NO;
|
|
|
|
#if HAVE_GRP_H
|
1999-02-20 21:19:15 +00:00
|
|
|
struct group *gp = getgrnam([str cString]);
|
|
|
|
|
|
|
|
if (gp)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
if (chown(cpath, -1, gp->gr_gid) == 0)
|
|
|
|
ok = YES;
|
1999-02-20 21:19:15 +00:00
|
|
|
}
|
|
|
|
#endif
|
1999-06-18 10:28:11 +00:00
|
|
|
if (ok == NO)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFileGroupOwnerAccountName to '%@'", str];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* __WIN32__ */
|
1999-02-20 21:19:15 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
num = [attributes objectForKey: NSFilePosixPermissions];
|
1999-02-20 21:19:15 +00:00
|
|
|
if (num)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
if (chmod(cpath, [num intValue]) != 0)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFilePosixPermissions to '%o'", [num intValue]];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
date = [attributes objectForKey: NSFileModificationDate];
|
1999-02-20 21:19:15 +00:00
|
|
|
if (date)
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
BOOL ok = NO;
|
1999-02-20 21:19:15 +00:00
|
|
|
struct stat sb;
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__) || defined(_POSIX_VERSION)
|
1999-02-20 21:19:15 +00:00
|
|
|
struct utimbuf ub;
|
1997-03-03 20:10:10 +00:00
|
|
|
#else
|
1999-02-20 21:19:15 +00:00
|
|
|
time_t ub[2];
|
1997-03-03 20:10:10 +00:00
|
|
|
#endif
|
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
if (stat(cpath, &sb) != 0)
|
1999-06-18 10:28:11 +00:00
|
|
|
ok = NO;
|
1999-02-20 21:19:15 +00:00
|
|
|
else
|
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__) || defined(_POSIX_VERSION)
|
1999-02-20 21:19:15 +00:00
|
|
|
ub.actime = sb.st_atime;
|
|
|
|
ub.modtime = [date timeIntervalSince1970];
|
1999-06-18 10:28:11 +00:00
|
|
|
ok = (utime(cpath, &ub) == 0);
|
1997-03-03 20:10:10 +00:00
|
|
|
#else
|
1999-02-20 21:19:15 +00:00
|
|
|
ub[0] = sb.st_atime;
|
|
|
|
ub[1] = [date timeIntervalSince1970];
|
1999-06-18 10:28:11 +00:00
|
|
|
ok = (utime((char*)cpath, ub) == 0);
|
1997-03-03 20:10:10 +00:00
|
|
|
#endif
|
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
if (ok == NO)
|
|
|
|
{
|
|
|
|
allOk = NO;
|
|
|
|
str = [NSString stringWithFormat:
|
|
|
|
@"Unable to change NSFileModificationDate to '%@'", date];
|
|
|
|
ASSIGN(lastError, str);
|
|
|
|
}
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-02-20 21:19:15 +00:00
|
|
|
return allOk;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Discovering directory contents
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
- (NSArray*) directoryContentsAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
NSDirectoryEnumerator *direnum;
|
|
|
|
NSMutableArray *content;
|
|
|
|
BOOL isDir;
|
|
|
|
IMP nxtImp;
|
|
|
|
IMP addImp;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
if (![self fileExistsAtPath: path isDirectory: &isDir] || !isDir)
|
1999-02-21 20:01:41 +00:00
|
|
|
return nil;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
direnum = [[NSDirectoryEnumerator alloc] initWithDirectoryPath: path
|
|
|
|
recurseIntoSubdirectories: NO
|
|
|
|
followSymlinks: NO
|
|
|
|
prefixFiles: NO];
|
1999-02-21 20:01:41 +00:00
|
|
|
content = [NSMutableArray arrayWithCapacity: 128];
|
1999-05-08 05:43:48 +00:00
|
|
|
|
|
|
|
nxtImp = [direnum methodForSelector: @selector(nextObject)];
|
1999-06-17 19:09:25 +00:00
|
|
|
addImp = [content methodForSelector: @selector(addObject: )];
|
1999-05-08 05:43:48 +00:00
|
|
|
|
|
|
|
while ((path = (*nxtImp)(direnum, @selector(nextObject))) != nil)
|
1999-06-17 19:09:25 +00:00
|
|
|
(*addImp)(content, @selector(addObject: ), path);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
RELEASE(direnum);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
return content;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSDirectoryEnumerator*) enumeratorAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
return AUTORELEASE([[NSDirectoryEnumerator alloc]
|
1999-05-08 05:43:48 +00:00
|
|
|
initWithDirectoryPath: path
|
|
|
|
recurseIntoSubdirectories: YES
|
|
|
|
followSymlinks: NO
|
|
|
|
prefixFiles: YES]);
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSArray*) subpathsAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
NSDirectoryEnumerator *direnum;
|
|
|
|
NSMutableArray *content;
|
|
|
|
BOOL isDir;
|
|
|
|
IMP nxtImp;
|
|
|
|
IMP addImp;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
if (![self fileExistsAtPath: path isDirectory: &isDir] || !isDir)
|
|
|
|
return nil;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
direnum = [[NSDirectoryEnumerator alloc]
|
|
|
|
initWithDirectoryPath: path
|
|
|
|
recurseIntoSubdirectories: YES
|
|
|
|
followSymlinks: NO
|
|
|
|
prefixFiles: YES];
|
|
|
|
content = [NSMutableArray arrayWithCapacity: 128];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
nxtImp = [direnum methodForSelector: @selector(nextObject)];
|
1999-06-17 19:09:25 +00:00
|
|
|
addImp = [content methodForSelector: @selector(addObject: )];
|
1999-05-08 05:43:48 +00:00
|
|
|
|
|
|
|
while ((path = (*nxtImp)(direnum, @selector(nextObject))) != nil)
|
1999-06-17 19:09:25 +00:00
|
|
|
(*addImp)(content, @selector(addObject: ), path);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
RELEASE(direnum);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
return content;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Symbolic-link operations
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) createSymbolicLinkAtPath: (NSString*)path
|
|
|
|
pathContent: (NSString*)otherPath
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
const char* lpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
const char* npath = [self fileSystemRepresentationWithPath: otherPath];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-11 09:21:38 +00:00
|
|
|
#ifdef __WIN32__
|
1999-05-08 05:43:48 +00:00
|
|
|
return NO;
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1999-05-08 05:43:48 +00:00
|
|
|
return (symlink(lpath, npath) == 0);
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSString*) pathContentOfSymbolicLinkAtPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
char lpath[PATH_MAX];
|
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
int llen = readlink(cpath, lpath, PATH_MAX-1);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
if (llen > 0)
|
|
|
|
return [self stringWithFileSystemRepresentation: lpath length: llen];
|
|
|
|
else
|
|
|
|
return nil;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Converting file-system representations
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (const char*) fileSystemRepresentationWithPath: (NSString*)path
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-11 09:21:38 +00:00
|
|
|
#if 0 && defined(__WIN32__)
|
1999-05-05 18:45:52 +00:00
|
|
|
unsigned len = [path length];
|
|
|
|
NSMutableData *d = [NSMutableData dataWithLength: len + 5];
|
|
|
|
char *fspath = (char*)[d mutableBytes];
|
|
|
|
|
|
|
|
[path getCString: &fspath[4]];
|
|
|
|
fspath[len+4] = '\0';
|
1997-09-13 17:52:31 +00:00
|
|
|
|
|
|
|
// Check if path specifies drive number or is current drive
|
1999-05-08 05:43:48 +00:00
|
|
|
if (fspath[4] && (fspath[5] == ': '))
|
1997-09-13 17:52:31 +00:00
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
fspath[0] = fspath[4];
|
|
|
|
fspath[1] = fspath[5];
|
|
|
|
fspath[2] = '\\';
|
|
|
|
fspath[3] = '\0';
|
1997-09-13 17:52:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-05-05 18:45:52 +00:00
|
|
|
fspath = &fspath[2];
|
|
|
|
fspath[0] = '\\';
|
|
|
|
fspath[1] = '\0';
|
1997-09-13 17:52:31 +00:00
|
|
|
}
|
1999-05-05 18:45:52 +00:00
|
|
|
return fspath;
|
1997-09-13 17:52:31 +00:00
|
|
|
#else
|
1999-02-21 20:01:41 +00:00
|
|
|
return [path cString];
|
1997-09-13 17:52:31 +00:00
|
|
|
#endif
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (NSString*) stringWithFileSystemRepresentation: (const char*)string
|
|
|
|
length: (unsigned int)len
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
return [NSString stringWithCString: string length: len];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end /* NSFileManager */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NSDirectoryEnumerator implementation
|
|
|
|
*/
|
|
|
|
|
|
|
|
@implementation NSDirectoryEnumerator
|
|
|
|
|
|
|
|
// Implementation dependent methods
|
|
|
|
|
|
|
|
/*
|
|
|
|
recurses into directory `path'
|
|
|
|
- pushes relative path (relative to root of search) on pathStack
|
|
|
|
- pushes system dir enumerator on enumPath
|
|
|
|
*/
|
1999-02-21 20:01:41 +00:00
|
|
|
- (void) recurseIntoDirectory: (NSString*)path relativeName: (NSString*)name
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
const char* cpath;
|
|
|
|
DIR* dir;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
cpath = [[NSFileManager defaultManager]
|
|
|
|
fileSystemRepresentationWithPath: path];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
dir = opendir(cpath);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
if (dir)
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
[pathStack addObject: name];
|
|
|
|
[enumStack addObject: [NSValue valueWithPointer: dir]];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
1999-02-21 20:01:41 +00:00
|
|
|
else
|
|
|
|
NSLog(@"Failed to recurse into directory '%@' - %s",
|
|
|
|
path, strerror(errno));
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
backtracks enumeration to the previous dir
|
|
|
|
- pops current dir relative path from pathStack
|
|
|
|
- pops system dir enumerator from enumStack
|
|
|
|
- sets currentFile* to nil
|
|
|
|
*/
|
1999-02-21 20:01:41 +00:00
|
|
|
- (void) backtrack
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
closedir((DIR*)[[enumStack lastObject] pointerValue]);
|
|
|
|
[enumStack removeLastObject];
|
|
|
|
[pathStack removeLastObject];
|
|
|
|
DESTROY(currentFileName);
|
|
|
|
DESTROY(currentFilePath);
|
1999-06-17 19:09:25 +00:00
|
|
|
DESTROY(fileAttributes);
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
finds the next file according to the top enumerator
|
|
|
|
- if there is a next file it is put in currentFile
|
|
|
|
- if the current file is a directory and if isRecursive calls
|
1999-05-08 05:43:48 +00:00
|
|
|
recurseIntoDirectory: currentFile
|
1997-03-03 20:10:10 +00:00
|
|
|
- if the current file is a symlink to a directory and if isRecursive
|
1999-05-08 05:43:48 +00:00
|
|
|
and isFollowing calls recurseIntoDirectory: currentFile
|
1997-03-03 20:10:10 +00:00
|
|
|
- if at end of current directory pops stack and attempts to
|
|
|
|
find the next entry in the parent
|
|
|
|
- sets currentFile to nil if there are no more files to enumerate
|
|
|
|
*/
|
1999-02-21 20:01:41 +00:00
|
|
|
- (void) findNextFile
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
NSFileManager* manager = [NSFileManager defaultManager];
|
|
|
|
DIR_enum_state* dir;
|
|
|
|
DIR_enum_item* dirbuf;
|
|
|
|
struct stat statbuf;
|
|
|
|
const char* cpath;
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
DESTROY(currentFileName);
|
|
|
|
DESTROY(currentFilePath);
|
1999-06-17 19:09:25 +00:00
|
|
|
DESTROY(fileAttributes);
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
while ([pathStack count])
|
|
|
|
{
|
|
|
|
dir = (DIR*)[[enumStack lastObject] pointerValue];
|
|
|
|
dirbuf = readdir(dir);
|
|
|
|
if (dirbuf)
|
|
|
|
{
|
|
|
|
/* Skip "." and ".." directory entries */
|
|
|
|
if (strcmp(dirbuf->d_name, ".") == 0 ||
|
|
|
|
strcmp(dirbuf->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
// Name of current file
|
|
|
|
currentFileName = [manager
|
1999-05-08 05:43:48 +00:00
|
|
|
stringWithFileSystemRepresentation: dirbuf->d_name
|
|
|
|
length: strlen(dirbuf->d_name)];
|
1999-02-21 20:01:41 +00:00
|
|
|
currentFileName = RETAIN([[pathStack lastObject]
|
1999-05-08 05:43:48 +00:00
|
|
|
stringByAppendingPathComponent: currentFileName]);
|
1999-02-21 20:01:41 +00:00
|
|
|
// Full path of current file
|
|
|
|
currentFilePath = RETAIN([topPath
|
1999-05-08 05:43:48 +00:00
|
|
|
stringByAppendingPathComponent: currentFileName]);
|
1999-02-21 20:01:41 +00:00
|
|
|
// Check if directory
|
1999-05-08 05:43:48 +00:00
|
|
|
cpath = [manager fileSystemRepresentationWithPath: currentFilePath];
|
1999-06-21 18:40:08 +00:00
|
|
|
if (flags.isRecursive == YES)
|
1999-02-21 20:01:41 +00:00
|
|
|
{
|
1999-06-21 18:40:08 +00:00
|
|
|
// Do not follow links
|
|
|
|
if (!flags.isFollowing)
|
|
|
|
{
|
|
|
|
if (lstat(cpath, &statbuf) != 0)
|
|
|
|
break;
|
|
|
|
// If link then return it as link
|
|
|
|
if (S_IFLNK == (S_IFMT & statbuf.st_mode))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (stat(cpath, &statbuf) != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (S_IFDIR == (S_IFMT & statbuf.st_mode))
|
|
|
|
{
|
|
|
|
[self recurseIntoDirectory: currentFilePath
|
|
|
|
relativeName: currentFileName];
|
|
|
|
}
|
1999-02-21 20:01:41 +00:00
|
|
|
}
|
|
|
|
break; // Got a file name - break out of loop
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self backtrack];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializing
|
|
|
|
|
1999-06-17 19:09:25 +00:00
|
|
|
- (id) initWithDirectoryPath: (NSString*)path
|
|
|
|
recurseIntoSubdirectories: (BOOL)recurse
|
|
|
|
followSymlinks: (BOOL)follow
|
|
|
|
prefixFiles: (BOOL)prefix
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
pathStack = [NSMutableArray new];
|
|
|
|
enumStack = [NSMutableArray new];
|
|
|
|
flags.isRecursive = recurse;
|
|
|
|
flags.isFollowing = follow;
|
|
|
|
|
|
|
|
topPath = RETAIN(path);
|
|
|
|
[self recurseIntoDirectory: path relativeName: @""];
|
|
|
|
|
|
|
|
return self;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
- (void) dealloc
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-01-20 13:28:28 +00:00
|
|
|
while ([pathStack count])
|
|
|
|
[self backtrack];
|
1997-03-03 20:10:10 +00:00
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
RELEASE(pathStack);
|
|
|
|
RELEASE(enumStack);
|
|
|
|
RELEASE(topPath);
|
1999-06-17 19:09:25 +00:00
|
|
|
TEST_RELEASE(currentFileName);
|
|
|
|
TEST_RELEASE(currentFilePath);
|
|
|
|
TEST_RELEASE(fileAttributes);
|
|
|
|
TEST_RELEASE(directoryAttributes);
|
1999-01-20 13:28:28 +00:00
|
|
|
[super dealloc];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Getting attributes
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
- (NSDictionary*) directoryAttributes
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-17 19:09:25 +00:00
|
|
|
if (directoryAttributes == nil)
|
|
|
|
{
|
|
|
|
directoryAttributes = [[NSFileManager defaultManager]
|
|
|
|
fileAttributesAtPath: topPath
|
1999-02-21 20:01:41 +00:00
|
|
|
traverseLink: flags.isFollowing];
|
1999-06-17 19:09:25 +00:00
|
|
|
RETAIN(directoryAttributes);
|
|
|
|
}
|
|
|
|
return directoryAttributes;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
- (NSDictionary*) fileAttributes
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-06-17 19:09:25 +00:00
|
|
|
if (fileAttributes == nil)
|
|
|
|
{
|
|
|
|
fileAttributes = [[NSFileManager defaultManager]
|
1999-02-21 20:01:41 +00:00
|
|
|
fileAttributesAtPath: currentFilePath
|
|
|
|
traverseLink: flags.isFollowing];
|
1999-06-17 19:09:25 +00:00
|
|
|
RETAIN(fileAttributes);
|
|
|
|
}
|
|
|
|
return fileAttributes;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Skipping subdirectories
|
|
|
|
|
1999-02-21 20:01:41 +00:00
|
|
|
- (void) skipDescendents
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
if ([pathStack count])
|
|
|
|
[self backtrack];
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enumerate next
|
|
|
|
|
1999-06-17 19:09:25 +00:00
|
|
|
- (id) nextObject
|
1997-03-03 20:10:10 +00:00
|
|
|
{
|
1999-02-21 20:01:41 +00:00
|
|
|
[self findNextFile];
|
|
|
|
return currentFileName;
|
1997-03-03 20:10:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end /* NSDirectoryEnumerator */
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
@implementation NSDirectoryEnumerator (PrivateMethods)
|
|
|
|
- (NSDictionary*) _attributesForCopy
|
|
|
|
{
|
|
|
|
return [[NSFileManager defaultManager]
|
|
|
|
_attributesAtPath: currentFilePath
|
|
|
|
traverseLink: flags.isFollowing
|
|
|
|
forCopy: YES];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
1997-03-03 20:10:10 +00:00
|
|
|
/*
|
|
|
|
* Attributes dictionary access
|
|
|
|
*/
|
|
|
|
|
|
|
|
@implementation NSDictionary(NSFileAttributes)
|
1998-12-03 16:20:31 +00:00
|
|
|
- (unsigned long long) fileSize
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFileSize] unsignedLongLongValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) fileType
|
|
|
|
{
|
|
|
|
return [self objectForKey: NSFileType];
|
|
|
|
}
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1998-12-03 16:20:31 +00:00
|
|
|
- (NSString*) fileOwnerAccountName
|
|
|
|
{
|
|
|
|
return [self objectForKey: NSFileOwnerAccountName];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long) fileOwnerAccountNumber
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFileOwnerAccountNumber] unsignedIntValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) fileGroupOwnerAccountName
|
|
|
|
{
|
|
|
|
return [self objectForKey: NSFileGroupOwnerAccountName];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long) fileGroupOwnerAccountNumber
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFileGroupOwnerAccountNumber] unsignedIntValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDate*) fileModificationDate
|
|
|
|
{
|
|
|
|
return [self objectForKey: NSFileModificationDate];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long) filePosixPermissions
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFilePosixPermissions] unsignedLongValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long) fileSystemNumber
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFileSystemNumber] unsignedLongValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long) fileSystemFileNumber
|
|
|
|
{
|
|
|
|
return [[self objectForKey: NSFileSystemFileNumber] unsignedLongValue];
|
|
|
|
}
|
|
|
|
@end
|
1998-02-05 22:06:20 +00:00
|
|
|
|
|
|
|
@implementation NSFileManager (PrivateMethods)
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) _copyFile: (NSString*)source
|
|
|
|
toFile: (NSString*)destination
|
|
|
|
handler: handler
|
1998-02-05 22:06:20 +00:00
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
NSDictionary* attributes;
|
|
|
|
int i, bufsize = 8096;
|
|
|
|
int sourceFd, destFd, fileSize, fileMode;
|
|
|
|
int rbytes, wbytes;
|
|
|
|
char buffer[bufsize];
|
|
|
|
|
|
|
|
/* Assumes source is a file and exists! */
|
|
|
|
NSAssert1 ([self fileExistsAtPath: source],
|
|
|
|
@"source file '%@' does not exist!", source);
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
attributes = [self _attributesAtPath: source traverseLink: NO forCopy: YES];
|
1999-05-08 05:43:48 +00:00
|
|
|
NSAssert1 (attributes, @"could not get the attributes for file '%@'",
|
|
|
|
source);
|
|
|
|
|
|
|
|
fileSize = [[attributes objectForKey: NSFileSize] intValue];
|
|
|
|
fileMode = [[attributes objectForKey: NSFilePosixPermissions] intValue];
|
|
|
|
|
|
|
|
/* Open the source file. In case of error call the handler. */
|
|
|
|
sourceFd = open ([self fileSystemRepresentationWithPath: source], O_RDONLY);
|
|
|
|
if (sourceFd < 0) {
|
|
|
|
if (handler) {
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
source, @"Path",
|
|
|
|
@"cannot open file for reading", @"Error",
|
|
|
|
nil];
|
|
|
|
return [handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the destination file. In case of error call the handler. */
|
|
|
|
destFd = open ([self fileSystemRepresentationWithPath: destination],
|
|
|
|
O_WRONLY|O_CREAT|O_TRUNC, fileMode);
|
|
|
|
if (destFd < 0) {
|
|
|
|
if (handler) {
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
destination, @"ToPath",
|
|
|
|
@"cannot open file for writing", @"Error",
|
|
|
|
nil];
|
|
|
|
close (sourceFd);
|
|
|
|
return [handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read bufsize bytes from source file and write them into the destination
|
|
|
|
file. In case of errors call the handler and abort the operation. */
|
|
|
|
for (i = 0; i < fileSize; i += rbytes) {
|
|
|
|
rbytes = read (sourceFd, buffer, bufsize);
|
|
|
|
if (rbytes < 0) {
|
|
|
|
if (handler) {
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
source, @"Path",
|
|
|
|
@"cannot read from file", @"Error",
|
|
|
|
nil];
|
|
|
|
close (sourceFd);
|
|
|
|
close (destFd);
|
|
|
|
return [handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
wbytes = write (destFd, buffer, rbytes);
|
|
|
|
if (wbytes != rbytes) {
|
|
|
|
if (handler) {
|
|
|
|
NSDictionary* errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
source, @"Path",
|
|
|
|
destination, @"ToPath",
|
|
|
|
@"cannot write to file", @"Error",
|
|
|
|
nil];
|
|
|
|
close (sourceFd);
|
|
|
|
close (destFd);
|
|
|
|
return [handler fileManager: self
|
|
|
|
shouldProceedAfterError: errorInfo];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close (sourceFd);
|
|
|
|
close (destFd);
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
return YES;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
- (BOOL) _copyPath: (NSString*)source
|
|
|
|
toPath: (NSString*)destination
|
|
|
|
handler: handler
|
1998-02-05 22:06:20 +00:00
|
|
|
{
|
1999-06-17 19:09:25 +00:00
|
|
|
NSDirectoryEnumerator *enumerator;
|
|
|
|
NSString *dirEntry;
|
1999-06-18 10:28:11 +00:00
|
|
|
CREATE_AUTORELEASE_POOL(pool);
|
1999-05-08 05:43:48 +00:00
|
|
|
|
|
|
|
enumerator = [self enumeratorAtPath: source];
|
1999-06-17 19:09:25 +00:00
|
|
|
while ((dirEntry = [enumerator nextObject]))
|
|
|
|
{
|
|
|
|
NSString *sourceFile;
|
|
|
|
NSString *fileType;
|
|
|
|
NSString *destinationFile;
|
|
|
|
NSDictionary *attributes;
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
attributes = [enumerator _attributesForCopy];
|
1999-05-08 05:43:48 +00:00
|
|
|
fileType = [attributes objectForKey: NSFileType];
|
|
|
|
sourceFile = [source stringByAppendingPathComponent: dirEntry];
|
|
|
|
destinationFile
|
1999-06-17 19:09:25 +00:00
|
|
|
= [destination stringByAppendingPathComponent: dirEntry];
|
1999-05-08 05:43:48 +00:00
|
|
|
|
|
|
|
[handler fileManager: self willProcessPath: sourceFile];
|
1999-06-17 19:09:25 +00:00
|
|
|
if ([fileType isEqual: NSFileTypeDirectory])
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
if (![self createDirectoryAtPath: destinationFile
|
1999-06-17 19:09:25 +00:00
|
|
|
attributes: attributes])
|
|
|
|
{
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSDictionary *errorInfo;
|
|
|
|
|
|
|
|
errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
destinationFile, @"Path",
|
1999-06-18 10:28:11 +00:00
|
|
|
lastError, @"Error", nil];
|
1999-05-08 05:43:48 +00:00
|
|
|
if (![handler fileManager: self
|
1999-06-17 19:09:25 +00:00
|
|
|
shouldProceedAfterError: errorInfo])
|
|
|
|
return NO;
|
|
|
|
}
|
1999-05-08 05:43:48 +00:00
|
|
|
else
|
1999-06-17 19:09:25 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
[enumerator skipDescendents];
|
1999-06-17 19:09:25 +00:00
|
|
|
if (![self _copyPath: sourceFile
|
|
|
|
toPath: destinationFile
|
|
|
|
handler: handler])
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ([fileType isEqual: NSFileTypeRegular])
|
|
|
|
{
|
|
|
|
if (![self _copyFile: sourceFile
|
|
|
|
toFile: destinationFile
|
|
|
|
handler: handler])
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else if ([fileType isEqual: NSFileTypeSymbolicLink])
|
|
|
|
{
|
1999-05-08 05:43:48 +00:00
|
|
|
if (![self createSymbolicLinkAtPath: destinationFile
|
1999-06-17 19:09:25 +00:00
|
|
|
pathContent: sourceFile])
|
|
|
|
{
|
|
|
|
if (handler)
|
|
|
|
{
|
|
|
|
NSDictionary *errorInfo
|
|
|
|
= [NSDictionary dictionaryWithObjectsAndKeys:
|
1999-05-08 05:43:48 +00:00
|
|
|
sourceFile, @"Path",
|
|
|
|
destinationFile, @"ToPath",
|
|
|
|
@"cannot create symbolic link", @"Error",
|
|
|
|
nil];
|
|
|
|
if (![handler fileManager: self
|
1999-06-17 19:09:25 +00:00
|
|
|
shouldProceedAfterError: errorInfo])
|
|
|
|
return NO;
|
|
|
|
}
|
1999-05-08 05:43:48 +00:00
|
|
|
else
|
1999-06-17 19:09:25 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-06-18 10:28:11 +00:00
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = [NSString stringWithFormat: @"cannot copy file type '%@'",
|
|
|
|
fileType];
|
|
|
|
ASSIGN(lastError, s);
|
|
|
|
NSLog(@"%@: %@", sourceFile, s);
|
|
|
|
continue;
|
1999-06-17 19:09:25 +00:00
|
|
|
}
|
1999-05-08 05:43:48 +00:00
|
|
|
[self changeFileAttributes: attributes atPath: destinationFile];
|
1999-06-17 19:09:25 +00:00
|
|
|
}
|
1999-06-18 10:28:11 +00:00
|
|
|
RELEASE(pool);
|
1998-02-05 22:06:20 +00:00
|
|
|
|
1999-05-08 05:43:48 +00:00
|
|
|
return YES;
|
1998-02-05 22:06:20 +00:00
|
|
|
}
|
|
|
|
|
1999-06-18 10:28:11 +00:00
|
|
|
- (NSDictionary*) _attributesAtPath: (NSString*)path
|
|
|
|
traverseLink: (BOOL)traverse
|
|
|
|
forCopy: (BOOL)copy
|
|
|
|
{
|
|
|
|
struct stat statbuf;
|
|
|
|
const char* cpath = [self fileSystemRepresentationWithPath: path];
|
|
|
|
int mode;
|
|
|
|
int count;
|
|
|
|
id values[12];
|
|
|
|
id keys[12] = {
|
|
|
|
NSFileSize,
|
|
|
|
NSFileModificationDate,
|
|
|
|
NSFileReferenceCount,
|
|
|
|
NSFileSystemNumber,
|
|
|
|
NSFileSystemFileNumber,
|
|
|
|
NSFileDeviceIdentifier,
|
|
|
|
NSFilePosixPermissions,
|
|
|
|
NSFileType,
|
|
|
|
NSFileOwnerAccountName,
|
|
|
|
NSFileGroupOwnerAccountName,
|
|
|
|
NSFileOwnerAccountNumber,
|
|
|
|
NSFileGroupOwnerAccountNumber
|
|
|
|
};
|
|
|
|
|
|
|
|
if (traverse)
|
|
|
|
{
|
|
|
|
if (stat(cpath, &statbuf) != 0)
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (lstat(cpath, &statbuf) != 0)
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
values[0] = [NSNumber numberWithUnsignedLongLong: statbuf.st_size];
|
|
|
|
values[1] = [NSDate dateWithTimeIntervalSince1970: statbuf.st_mtime];
|
|
|
|
values[2] = [NSNumber numberWithUnsignedInt: statbuf.st_nlink];
|
|
|
|
values[3] = [NSNumber numberWithUnsignedLong: statbuf.st_dev];
|
|
|
|
values[4] = [NSNumber numberWithUnsignedLong: statbuf.st_ino];
|
|
|
|
values[5] = [NSNumber numberWithUnsignedInt: statbuf.st_dev];
|
|
|
|
values[6] = [NSNumber numberWithUnsignedInt: statbuf.st_mode];
|
|
|
|
|
|
|
|
mode = statbuf.st_mode & S_IFMT;
|
|
|
|
|
|
|
|
if (mode == S_IFREG)
|
|
|
|
values[7] = NSFileTypeRegular;
|
|
|
|
else if (mode == S_IFDIR)
|
|
|
|
values[7] = NSFileTypeDirectory;
|
|
|
|
else if (mode == S_IFCHR)
|
|
|
|
values[7] = NSFileTypeCharacterSpecial;
|
|
|
|
else if (mode == S_IFBLK)
|
|
|
|
values[7] = NSFileTypeBlockSpecial;
|
|
|
|
#ifdef S_IFLNK
|
|
|
|
else if (mode == S_IFLNK)
|
|
|
|
values[7] = NSFileTypeSymbolicLink;
|
|
|
|
#endif
|
|
|
|
else if (mode == S_IFIFO)
|
|
|
|
values[7] = NSFileTypeFifo;
|
|
|
|
#ifdef S_IFSOCK
|
|
|
|
else if (mode == S_IFSOCK)
|
|
|
|
values[7] = NSFileTypeSocket;
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
values[7] = NSFileTypeUnknown;
|
|
|
|
|
|
|
|
if (copy == NO)
|
|
|
|
{
|
|
|
|
#if HAVE_PWD_H
|
|
|
|
{
|
|
|
|
struct passwd *pw;
|
|
|
|
|
|
|
|
pw = getpwuid(statbuf.st_uid);
|
|
|
|
|
|
|
|
if (pw)
|
|
|
|
{
|
|
|
|
values[8] = [NSString stringWithCString: pw->pw_name];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[8] = @"UnknownUser";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
values[8] = @"UnknownUser";
|
|
|
|
#endif /* HAVE_PWD_H */
|
|
|
|
|
|
|
|
#if HAVE_GRP_H
|
|
|
|
{
|
|
|
|
struct group *gp;
|
|
|
|
|
|
|
|
setgrent();
|
|
|
|
while ((gp = getgrent()) != 0)
|
|
|
|
{
|
|
|
|
if (gp->gr_gid == statbuf.st_gid)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (gp)
|
|
|
|
{
|
|
|
|
values[9] = [NSString stringWithCString: gp->gr_name];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[9] = @"UnknownGroup";
|
|
|
|
}
|
|
|
|
endgrent();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
values[9] = @"UnknownGroup";
|
|
|
|
#endif
|
|
|
|
values[10] = [NSNumber numberWithUnsignedInt: statbuf.st_uid];
|
|
|
|
values[11] = [NSNumber numberWithUnsignedInt: statbuf.st_gid];
|
|
|
|
count = 12;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSString *u = NSUserName();
|
|
|
|
|
|
|
|
count = 8; /* No ownership details needed. */
|
|
|
|
/*
|
|
|
|
* If we are running setuid to root - we need to specify the user
|
|
|
|
* to be the owner of copied files.
|
|
|
|
*/
|
|
|
|
if (geteuid() == 0 && [@"root" isEqualToString: u] == NO)
|
|
|
|
{
|
|
|
|
values[count++] = u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [NSDictionary dictionaryWithObjects: values
|
|
|
|
forKeys: keys
|
|
|
|
count: count];
|
|
|
|
}
|
|
|
|
|
1998-02-05 22:06:20 +00:00
|
|
|
@end /* NSFileManager (PrivateMethods) */
|