diff options
author | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2010-07-26 17:00:52 +0000 |
---|---|---|
committer | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2010-07-26 17:00:52 +0000 |
commit | 5d588c34c079b3ac0333c2ef5eed6efb76b31b88 (patch) | |
tree | 294488e6da48402e4276603ef3853f39f67b9aaf /Foundation | |
parent | 4d56ee54c0f2b36d0e96db4a74e1b84d539df29d (diff) |
[Author: dmaclach]
Added GTMNSFileHandle+UniqueName for easily and safely creating temporary files and
unique directory names.
Modified some tests to use the new calls.
R=thomasvl
DELTA=420 (397 added, 16 deleted, 7 changed)
Diffstat (limited to 'Foundation')
-rw-r--r-- | Foundation/GTMNSFileHandle+UniqueName.h | 81 | ||||
-rw-r--r-- | Foundation/GTMNSFileHandle+UniqueName.m | 120 | ||||
-rw-r--r-- | Foundation/GTMNSFileHandle+UniqueNameTest.m | 167 | ||||
-rw-r--r-- | Foundation/GTMNSFileManager+PathTest.m | 19 | ||||
-rw-r--r-- | Foundation/GTMPathTest.m | 11 |
5 files changed, 376 insertions, 22 deletions
diff --git a/Foundation/GTMNSFileHandle+UniqueName.h b/Foundation/GTMNSFileHandle+UniqueName.h new file mode 100644 index 0000000..38197cd --- /dev/null +++ b/Foundation/GTMNSFileHandle+UniqueName.h @@ -0,0 +1,81 @@ +// +// GTMNSFileHandle+UniqueName.h +// +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import <Foundation/Foundation.h> +#import "GTMDefines.h" + +@interface NSFileHandle (GTMFileHandleUniqueNameAdditions) + +// Creates a read/write temporary file in NSTemporaryDirectory with mode 0600. +// The template should be similar to the template passed to mkstemp. +// If there is an extension on the nameTemplate it will remain. An example +// template is "MyAppXXXXXX.txt". +// If |path| is not nil, it will contain the derived path for the file. +// The file descriptor wrapped by the NSFileHandle will be closed on dealloc. ++ (id)gtm_fileHandleForTemporaryFileBasedOn:(NSString *)nameTemplate + finalPath:(NSString **)path; + +// Return an opened read/write file handle with mode 0600 based on a template. +// The template should be similar to the template passed to mkstemp. +// If there is an extension on the pathTemplate it will remain. An example +// template is "/Applications/MyAppXXXXXX.txt". +// If |path| is not nil, it will contain the derived path for the file. +// The file descriptor wrapped by the NSFileHandle will be closed on dealloc. ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)pathTemplate + finalPath:(NSString **)path; + +// Same as fileHandleWithUniqueNameBasedOn:finalName: but splits up the +// template from the directory. ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSString *)directory + finalPath:(NSString **)path; + + +// Same as fileHandleWithUniqueNameBasedOn:inDirectory:finalName: but finds +// the directory using the |directory| and |mask| arguments. ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSSearchPathDirectory)directory + domainMask:(NSSearchPathDomainMask)mask + finalPath:(NSString **)path; +@end + +@interface NSFileManager (GTMFileManagerUniqueNameAdditions) + +// Creates a new directory in NSTemporaryDirectory with mode 0700. +// The template should be similar to the template passed to mkdtemp. +- (NSString *)gtm_createTemporaryDirectoryBasedOn:(NSString *)nameTemplate; + +// Return the path to a directory with mode 0700 based on a template. +// The template should be similar to the template passed to mkdtemp. +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)nameTemplate; + +// Same as createDirectoryWithUniqueNameBasedOn: but splits up the +// template from the directory. +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)pathTemplate + inDirectory:(NSString *)directory; + +// Same as createDirectoryWithUniqueNameBasedOn:inDirectory: but finds +// the directory using the |directory| and |mask| arguments. +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)pathTemplate + inDirectory:(NSSearchPathDirectory)directory + domainMask:(NSSearchPathDomainMask)mask; +@end + +// Same template as you would pass to mktemp. Note that this has the same +// potential race conditions for use with file creation as mktemp does. +GTM_EXTERN NSString *GTMUniqueFileObjectPathBasedOn(NSString *pathTemplate); diff --git a/Foundation/GTMNSFileHandle+UniqueName.m b/Foundation/GTMNSFileHandle+UniqueName.m new file mode 100644 index 0000000..fc790ee --- /dev/null +++ b/Foundation/GTMNSFileHandle+UniqueName.m @@ -0,0 +1,120 @@ +// +// GTMNSFileHandle+UniqueName.m +// +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSFileHandle+UniqueName.h" +#include <unistd.h> + +NSString *GTMUniqueFileObjectPathBasedOn(NSString *pathTemplate) { + if (!pathTemplate) return nil; + char *pathTemplateCString = strdup([pathTemplate fileSystemRepresentation]); + if (!pathTemplateCString) return nil; + char *newCName = mktemp(pathTemplateCString); + NSString *newName = newCName ? [NSString stringWithUTF8String:newCName] : nil; + free(pathTemplateCString); + return newName; +} + +@implementation NSFileHandle (GTMFileHandleUniqueNameAdditions) + ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)pathTemplate + finalPath:(NSString **)path { + if (!pathTemplate) return nil; + NSString *extension = [pathTemplate pathExtension]; + char *pathTemplateCString = strdup([pathTemplate fileSystemRepresentation]); + if (!pathTemplateCString) return nil; + int fileDescriptor = mkstemps(pathTemplateCString, (int)[extension length]); + if (fileDescriptor == -1) { + free(pathTemplateCString); + return nil; + } + NSFileHandle *handle + = [[[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor + closeOnDealloc:YES] autorelease]; + if (handle && path) { + *path = [NSString stringWithUTF8String:pathTemplateCString]; + } + free(pathTemplateCString); + return handle; +} + ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSString *)directory + finalPath:(NSString **)path { + NSString *fullPath = [directory stringByAppendingPathComponent:nameTemplate]; + return [self gtm_fileHandleWithUniqueNameBasedOn:fullPath finalPath:path]; +} + ++ (id)gtm_fileHandleWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSSearchPathDirectory)directory + domainMask:(NSSearchPathDomainMask)mask + finalPath:(NSString **)path { + NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(directory, + mask, + YES); + if ([searchPaths count] == 0) return nil; + NSString *searchPath = [searchPaths objectAtIndex:0]; + return [self gtm_fileHandleWithUniqueNameBasedOn:nameTemplate + inDirectory:searchPath + finalPath:path]; +} + ++ (id)gtm_fileHandleForTemporaryFileBasedOn:(NSString *)nameTemplate + finalPath:(NSString **)path { + return [self gtm_fileHandleWithUniqueNameBasedOn:nameTemplate + inDirectory:NSTemporaryDirectory() + finalPath:path]; +} + +@end + +@implementation NSFileManager (GTMFileManagerUniqueNameAdditions) + +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)pathTemplate { + if (!pathTemplate) return nil; + char *pathTemplateCString = strdup([pathTemplate fileSystemRepresentation]); + if (!pathTemplateCString) return nil; + char *outCName = mkdtemp(pathTemplateCString); + NSString *outName = outCName ? [NSString stringWithUTF8String:outCName] : nil; + free(pathTemplateCString); + return outName; +} + +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSString *)directory { + NSString *fullPath = [directory stringByAppendingPathComponent:nameTemplate]; + return [self gtm_createDirectoryWithUniqueNameBasedOn:fullPath]; +} + +- (NSString *)gtm_createDirectoryWithUniqueNameBasedOn:(NSString *)nameTemplate + inDirectory:(NSSearchPathDirectory)directory + domainMask:(NSSearchPathDomainMask)mask { + NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(directory, + mask, + YES); + if ([searchPaths count] == 0) return nil; + NSString *searchPath = [searchPaths objectAtIndex:0]; + return [self gtm_createDirectoryWithUniqueNameBasedOn:nameTemplate + inDirectory:searchPath]; +} + +- (NSString *)gtm_createTemporaryDirectoryBasedOn:(NSString *)nameTemplate { + return [self gtm_createDirectoryWithUniqueNameBasedOn:nameTemplate + inDirectory:NSTemporaryDirectory()]; +} + +@end diff --git a/Foundation/GTMNSFileHandle+UniqueNameTest.m b/Foundation/GTMNSFileHandle+UniqueNameTest.m new file mode 100644 index 0000000..833edc9 --- /dev/null +++ b/Foundation/GTMNSFileHandle+UniqueNameTest.m @@ -0,0 +1,167 @@ +// +// GTMNSFileHandle+UniqueNameTest.m +// +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import "GTMNSFileHandle+UniqueName.h" + +@interface GTMNSFileHandle_UniqueNameTest : GTMTestCase +@end + +@implementation GTMNSFileHandle_UniqueNameTest + +- (void)testGTMUniqueFileObjectPathBasedOn { + NSString *path = GTMUniqueFileObjectPathBasedOn(nil); + STAssertNil(path, nil); + path = GTMUniqueFileObjectPathBasedOn(@"/System"); + STAssertNil(path, nil); + path = GTMUniqueFileObjectPathBasedOn(@"/Users/HappyXXXXXX"); + STAssertTrue([path hasPrefix:@"/Users/Happy"], nil); + STAssertNotEqualObjects(path, @"/Users/HappyXXXXXX", nil); +} + +- (void)testFileHandleWithUniqueNameBasedOnFinalPath { + NSFileHandle *handle + = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:nil + finalPath:nil]; + STAssertNil(handle, nil); + + // Try and create a file where we shouldn't be able to. + NSString *path = nil; + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:@"/System/HappyXXX.txt" + finalPath:&path]; + STAssertNil(handle, nil); + STAssertNil(path, nil); + + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *tempDir + = [fm gtm_createTemporaryDirectoryBasedOn:@"GTMNSFileHandle_UniqueNameTestXXXXXX"]; + STAssertNotNil(tempDir, nil); + BOOL isDirectory = NO; + STAssertTrue([fm fileExistsAtPath:tempDir isDirectory:&isDirectory] + && isDirectory, nil); + + // Test with extension + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:@"HappyXXX.txt" + inDirectory:tempDir + finalPath:&path]; + STAssertNotNil(handle, nil); + STAssertEqualObjects([path pathExtension], @"txt", nil); + STAssertTrue([fm fileExistsAtPath:path], nil); + + // Test without extension + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:@"HappyXXX" + inDirectory:tempDir + finalPath:&path]; + STAssertNotNil(handle, nil); + STAssertEqualObjects([path pathExtension], @"", nil); + STAssertTrue([fm fileExistsAtPath:path], nil); + + // Test passing in same name twice + NSString *fullPath = [tempDir stringByAppendingPathComponent:@"HappyXXX"]; + NSString *newPath = nil; + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:fullPath + finalPath:&newPath]; + STAssertNotNil(handle, nil); + STAssertNotNil(newPath, nil); + STAssertNotEqualObjects(path, newPath, nil); + STAssertTrue([fm fileExistsAtPath:newPath], nil); + + // Test passing in same name twice with no template + fullPath = [tempDir stringByAppendingPathComponent:@"Sad"]; + newPath = nil; + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:fullPath + finalPath:&newPath]; + STAssertNotNil(handle, nil); + STAssertNotNil(newPath, nil); + + newPath = nil; + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:fullPath + finalPath:&newPath]; + STAssertNil(handle, nil); + STAssertNil(newPath, nil); + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeFileAtPath:tempDir handler:nil]; +#else // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeItemAtPath:tempDir error:nil]; +#endif + +} + +- (void)testFileHandleWithUniqueNameBasedOnInDirectorySearchMaskFinalPath { + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *path = nil; + NSFileHandle *handle + = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:nil + inDirectory:NSCachesDirectory + domainMask:NSUserDomainMask + finalPath:&path]; + STAssertNil(handle, nil); + STAssertNil(path, nil); + + handle = [NSFileHandle gtm_fileHandleWithUniqueNameBasedOn:@"HappyXXX.txt" + inDirectory:NSCachesDirectory + domainMask:NSUserDomainMask + finalPath:&path]; + STAssertNotNil(handle, nil); + STAssertNotNil(path, nil); + STAssertTrue([fm fileExistsAtPath:path], nil); +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeFileAtPath:path handler:nil]; +#else // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeItemAtPath:path error:nil]; +#endif +} + +@end + +@interface GTMNSFileManager_UniqueNameTest : GTMTestCase +@end + +@implementation GTMNSFileManager_UniqueNameTest + +- (void)testCreateDirectoryWithUniqueNameBasedOn { + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *path + = [fm gtm_createDirectoryWithUniqueNameBasedOn:@"/System/HappyXXX.txt"]; + STAssertNil(path, nil); +} + +- (void)testCreateDirectoryWithUniqueNameBasedOnInDirectorySearchMask { + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *path = [fm gtm_createDirectoryWithUniqueNameBasedOn:nil + inDirectory:NSCachesDirectory + domainMask:NSUserDomainMask]; + STAssertNil(path, nil); + + path = [fm gtm_createDirectoryWithUniqueNameBasedOn:@"HappyXXX.txt" + inDirectory:NSCachesDirectory + domainMask:NSUserDomainMask]; + STAssertNotNil(path, nil); + BOOL isDirectory = NO; + STAssertTrue([fm fileExistsAtPath:path isDirectory:&isDirectory] + && isDirectory, nil); +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeFileAtPath:path handler:nil]; +#else // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + [fm removeItemAtPath:path error:nil]; +#endif +} + +@end + diff --git a/Foundation/GTMNSFileManager+PathTest.m b/Foundation/GTMNSFileManager+PathTest.m index 2ce8683..db51c9b 100644 --- a/Foundation/GTMNSFileManager+PathTest.m +++ b/Foundation/GTMNSFileManager+PathTest.m @@ -18,6 +18,7 @@ #import "GTMSenTestCase.h" #import "GTMNSFileManager+Path.h" +#import "GTMNSFileHandle+UniqueName.h" @interface GTMNSFileManager_PathTest : GTMTestCase { NSString *baseDir_; @@ -28,22 +29,10 @@ - (void)setUp { // make a directory to scribble in - NSString *base = NSTemporaryDirectory(); - base = [base stringByAppendingPathComponent:@"GTMNSFileManager_PathTest"]; NSFileManager *fm = [NSFileManager defaultManager]; - STAssertFalse([fm fileExistsAtPath:base], @"File exists at %@", base); -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 - NSError *error = nil; - STAssertTrue([fm createDirectoryAtPath:base - withIntermediateDirectories:YES - attributes:nil - error:&error], - @"Unable to create %@: %@", base, error); -#else - STAssertTrue([fm createDirectoryAtPath:base attributes:nil], - @"Unable to create %@", base); -#endif - baseDir_ = [base retain]; + baseDir_ + = [[fm gtm_createTemporaryDirectoryBasedOn:@"GTMNSFileManager_PathTestXXXXXX"] + retain]; } - (void)tearDown { diff --git a/Foundation/GTMPathTest.m b/Foundation/GTMPathTest.m index 1e58f50..59e3199 100644 --- a/Foundation/GTMPathTest.m +++ b/Foundation/GTMPathTest.m @@ -19,6 +19,7 @@ #import "GTMSenTestCase.h" #import "GTMPath.h" #import "GTMUnitTestDevLog.h" +#import "GTMNSFileHandle+UniqueName.h" #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 // NSFileManager has improved substantially in Leopard and beyond, so GTMPath @@ -33,15 +34,11 @@ @implementation GTMPathTest - (void)setUp { - NSString *tmp = NSTemporaryDirectory(); - STAssertNotNil(tmp, nil); + NSFileManager *mgr = [NSFileManager defaultManager]; + testDirectory_ + = [[mgr gtm_createTemporaryDirectoryBasedOn:@"GTMPathTestXXXXXX"] retain]; - testDirectory_ = [[tmp stringByAppendingPathComponent:@"GTMPathTest"] retain]; STAssertNotNil(testDirectory_, nil); - - NSFileManager *mgr = [NSFileManager defaultManager]; - BOOL created = [mgr createDirectoryAtPath:testDirectory_ attributes:nil]; - STAssertTrue(created, nil); } - (void)tearDown { |