Revision: 10949
          http://bibdesk.svn.sourceforge.net/bibdesk/?rev=10949&view=rev
Author:   amaxwell
Date:     2007-08-19 22:12:19 -0700 (Sun, 19 Aug 2007)

Log Message:
-----------
Unify some temporary file and directory creation code by moving it all into the 
NSFileManager category.  Separate methods by directory creation (file created 
by manager) and file name creation (file created by caller).  Hopefully the 
names are clear enough.

The temporary directory creation needs to be thread safe, and guarded against 
race conditions, so try to ensure this.

The temporary file creation methods are used only on the main thread, so at 
least aren't subject to a race between threads.  Security implications aren't 
significant enough for me to worry about at this point.

Modified Paths:
--------------
    trunk/bibdesk/BDSKDocumentController.m
    trunk/bibdesk/BDSKEntrezGroupServer.m
    trunk/bibdesk/BDSKErrorEditor.m
    trunk/bibdesk/BDSKScriptGroup.m
    trunk/bibdesk/BDSKShellTask.m
    trunk/bibdesk/BDSKTeXTask.m
    trunk/bibdesk/BDSKURLGroup.m
    trunk/bibdesk/BibAppController.h
    trunk/bibdesk/BibAppController.m
    trunk/bibdesk/BibDocument.m
    trunk/bibdesk/BibDocument_DataSource.m
    trunk/bibdesk/BibFiler.m
    trunk/bibdesk/NSFileManager_BDSKExtensions.h
    trunk/bibdesk/NSFileManager_BDSKExtensions.m

Modified: trunk/bibdesk/BDSKDocumentController.m
===================================================================
--- trunk/bibdesk/BDSKDocumentController.m      2007-08-19 22:17:43 UTC (rev 
10948)
+++ trunk/bibdesk/BDSKDocumentController.m      2007-08-20 05:12:19 UTC (rev 
10949)
@@ -54,6 +54,7 @@
 #import "NSError_BDSKExtensions.h"
 #import "BDSKSearchGroup.h"
 #import "BDSKGroupsArray.h"
+#import "NSFileManager_BDSKExtensions.h"
 
 @implementation BDSKDocumentController
 
@@ -275,7 +276,7 @@
     // @@ we could also use [[NSApp delegate] temporaryFilePath:[filePath 
lastPathComponent] createDirectory:NO];
     // or [[NSFileManager defaultManager] uniqueFilePath:[filePath 
lastPathComponent] createDirectory:NO];
     // or move aside the original file
-    NSString *tmpFilePath = [[[NSApp delegate] temporaryFilePath:nil 
createDirectory:NO] stringByAppendingPathExtension:@"bib"];
+    NSString *tmpFilePath = [[[NSFileManager defaultManager] 
temporaryFileWithBasename:nil] stringByAppendingPathExtension:@"bib"];
     NSURL *tmpFileURL = [NSURL fileURLWithPath:tmpFilePath];
     NSData *data = [fileString dataUsingEncoding:encoding];
     

Modified: trunk/bibdesk/BDSKEntrezGroupServer.m
===================================================================
--- trunk/bibdesk/BDSKEntrezGroupServer.m       2007-08-19 22:17:43 UTC (rev 
10948)
+++ trunk/bibdesk/BDSKEntrezGroupServer.m       2007-08-20 05:12:19 UTC (rev 
10949)
@@ -57,6 +57,7 @@
 #import <WebKit/WebKit.h>
 #import "BDSKServerInfo.h"
 #import "NSError_BDSKExtensions.h"
+#import "NSFileManager_BDSKExtensions.h"
 
 @implementation BDSKEntrezGroupServer
 
@@ -284,7 +285,7 @@
         [URLDownload cancel];
     [URLDownload release];
     URLDownload = [[WebDownload alloc] initWithRequest:request delegate:self];
-    [URLDownload setDestination:[[NSApp delegate] temporaryFilePath:nil 
createDirectory:NO] allowOverwrite:NO];
+    [URLDownload setDestination:[[NSFileManager defaultManager] 
temporaryFileWithBasename:nil] allowOverwrite:NO];
 }
 
 - (void)download:(NSURLDownload *)download didCreateDestination:(NSString 
*)path

Modified: trunk/bibdesk/BDSKErrorEditor.m
===================================================================
--- trunk/bibdesk/BDSKErrorEditor.m     2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BDSKErrorEditor.m     2007-08-20 05:12:19 UTC (rev 10949)
@@ -207,7 +207,7 @@
     
     if(fileName == nil){
         OBASSERT(data != nil && document != nil);
-        [self setFileName:[[NSApp delegate] temporaryFilePath:[document 
displayName] createDirectory:NO]];
+        [self setFileName:[[NSFileManager defaultManager] 
temporaryFileWithBasename:[document displayName]]];
         [data writeToFile:fileName atomically:YES];
         [data release];
         data = nil;
@@ -234,7 +234,7 @@
 - (IBAction)reopenDocument:(id)sender{
     NSString *expandedFileName = [[self fileName] 
stringByExpandingTildeInPath];
     
-    expandedFileName = [[NSFileManager defaultManager] 
uniqueFilePath:expandedFileName createDirectory:NO];
+    expandedFileName = [[NSFileManager defaultManager] 
uniqueFilePathWithName:[expandedFileName lastPathComponent] 
atPath:[expandedFileName stringByDeletingLastPathComponent]];
     NSURL *expandedFileURL = [NSURL fileURLWithPath:expandedFileName];
     
     // write this out with the user's default encoding, so the 
openDocumentWithContentsOfFile is more likely to succeed

Modified: trunk/bibdesk/BDSKScriptGroup.m
===================================================================
--- trunk/bibdesk/BDSKScriptGroup.m     2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BDSKScriptGroup.m     2007-08-20 05:12:19 UTC (rev 10949)
@@ -91,7 +91,7 @@
         scriptType = type;
         failedDownload = NO;
         
-        workingDirPath = [[[NSApp delegate] temporaryFilePath:nil 
createDirectory:YES] retain];
+        workingDirPath = [[[NSFileManager defaultManager] 
makeTemporaryDirectoryWithBasename:nil] retain];
         
         OFSimpleLockInit(&processingLock);
         OFSimpleLockInit(&currentTaskLock);

Modified: trunk/bibdesk/BDSKShellTask.m
===================================================================
--- trunk/bibdesk/BDSKShellTask.m       2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BDSKShellTask.m       2007-08-20 05:12:19 UTC (rev 10949)
@@ -38,6 +38,7 @@
 
 #import "BDSKShellTask.h"
 #import "BibAppController.h"
+#import "NSFileManager_BDSKExtensions.h"
 
 volatile int caughtSignal = 0;
 
@@ -114,7 +115,7 @@
 - (NSData *)privateRunShellCommand:(NSString *)cmd withInputString:(NSString 
*)input{
     NSFileManager *fm = [NSFileManager defaultManager];
     NSString *shellPath = @"/bin/sh";
-    NSString *shellScriptPath = [[NSApp delegate] 
temporaryFilePath:@"shellscript" createDirectory:NO];
+    NSString *shellScriptPath = [[NSFileManager defaultManager] 
temporaryFileWithBasename:@"shellscript"];
     NSString *script;
     NSData *scriptData;
     NSMutableDictionary *currentAttributes;

Modified: trunk/bibdesk/BDSKTeXTask.m
===================================================================
--- trunk/bibdesk/BDSKTeXTask.m 2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BDSKTeXTask.m 2007-08-20 05:12:19 UTC (rev 10949)
@@ -94,13 +94,13 @@
 @implementation BDSKTeXTask
 
 - (id)init{
-    NSString *tmpDirPath = [[NSApp delegate] temporaryFilePath:@"tmpbib" 
createDirectory:YES];
+    NSString *tmpDirPath = [[NSFileManager defaultManager] 
makeTemporaryDirectoryWithBasename:@"tmpbib"];
        self = [self initWithWorkingDirPath:tmpDirPath fileName:@"tmpbib"];
        return self;
 }
 
 - (id)initWithFileName:(NSString *)newFileName{
-    NSString *tmpDirPath = [[NSApp delegate] temporaryFilePath:newFileName 
createDirectory:YES];
+    NSString *tmpDirPath = [[NSFileManager defaultManager] 
makeTemporaryDirectoryWithBasename:newFileName];
        self = [self initWithWorkingDirPath:tmpDirPath fileName:newFileName];
        return self;
 }

Modified: trunk/bibdesk/BDSKURLGroup.m
===================================================================
--- trunk/bibdesk/BDSKURLGroup.m        2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BDSKURLGroup.m        2007-08-20 05:12:19 UTC (rev 10949)
@@ -50,6 +50,7 @@
 #import "BDSKPublicationsArray.h"
 #import "BDSKMacroResolver.h"
 #import "BDSKItemSearchIndexes.h"
+#import "NSFileManager_BDSKExtensions.h"
 
 @implementation BDSKURLGroup
 
@@ -166,7 +167,7 @@
             [URLDownload cancel];
         [URLDownload release];
         URLDownload = [[WebDownload alloc] initWithRequest:request 
delegate:self];
-        [URLDownload setDestination:[[NSApp delegate] temporaryFilePath:nil 
createDirectory:NO] allowOverwrite:NO];
+        [URLDownload setDestination:[[NSFileManager defaultManager] 
temporaryFileWithBasename:nil] allowOverwrite:NO];
         isRetrieving = YES;
     }
 }

Modified: trunk/bibdesk/BibAppController.h
===================================================================
--- trunk/bibdesk/BibAppController.h    2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BibAppController.h    2007-08-20 05:12:19 UTC (rev 10949)
@@ -69,7 +69,6 @@
 }
 
 - (void)copyAllExportTemplatesToApplicationSupportAndOverwrite:(BOOL)overwrite;
-- (NSString *)temporaryFilePath:(NSString *)fileName 
createDirectory:(BOOL)create;
 
 - (NSMenu *)groupSortMenu;
        

Modified: trunk/bibdesk/BibAppController.m
===================================================================
--- trunk/bibdesk/BibAppController.m    2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BibAppController.m    2007-08-20 05:12:19 UTC (rev 10949)
@@ -113,35 +113,10 @@
     return array;
 }
 
-static NSString *temporaryBaseDirectory = nil;
-static void createTemporaryDirectory()
-{
-    OBASSERT([NSThread inMainThread]);
-    // somewhere in /var/tmp, generally; contents moved to Trash on relaunch
-    NSString *temporaryPath = NSTemporaryDirectory();
-    
-    // chewable items are automatically cleaned up at restart
-    FSRef fileRef;
-    OSErr err = FSFindFolder(kUserDomain, kChewableItemsFolderType, TRUE, 
&fileRef);
-    
-    NSURL *fileURL = nil;
-    if (noErr == err)
-        fileURL = [(id)CFURLCreateFromFSRef(CFAllocatorGetDefault(), &fileRef) 
autorelease];
-    
-    if (NULL != fileURL)
-        temporaryPath = [fileURL path];
-    
-    temporaryBaseDirectory = [[[NSFileManager defaultManager] 
uniqueFilePath:[temporaryPath stringByAppendingPathComponent:@"bibdesk"] 
-    createDirectory:YES] copy];    
-}
-
 + (void)initialize
 {
     OBINITIALIZE;
-    
-    // do this now to avoid race condition instead of creating it lazily and 
locking
-    createTemporaryDirectory();    
-    
+        
     // make sure we use Spotlight's plugins on 10.4 and later
     SKLoadDefaultExtractorPlugIns();
 
@@ -497,21 +472,6 @@
     [[NSNotificationCenter defaultCenter] 
postNotificationName:OAFlagsChangedNotification object:[NSApp currentEvent]];
 }
 
-#pragma mark Temporary files and directories
-
-- (NSString *)temporaryFilePath:(NSString *)fileName 
createDirectory:(BOOL)create{
-       if(nil == fileName) {
-        // NSProcessInfo isn't thread-safe, so use CFUUID instead of 
globallyUniqueString
-        CFAllocatorRef alloc = CFAllocatorGetDefault();
-        CFUUIDRef uuid = CFUUIDCreate(alloc);
-        fileName = [(id)CFUUIDCreateString(alloc, uuid) autorelease];
-        CFRelease(uuid);
-    }
-       NSString *tmpFilePath = [temporaryBaseDirectory 
stringByAppendingPathComponent:fileName];
-       return [[NSFileManager defaultManager] uniqueFilePath:tmpFilePath 
-                                                                               
  createDirectory:create];
-}
-
 #pragma mark Menu stuff
 
 - (NSMenu *)groupSortMenu {

Modified: trunk/bibdesk/BibDocument.m
===================================================================
--- trunk/bibdesk/BibDocument.m 2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BibDocument.m 2007-08-20 05:12:19 UTC (rev 10949)
@@ -1456,7 +1456,7 @@
         NSString *string = [[NSString alloc] initWithData:data 
encoding:encoding];
         if([string canBeConvertedToEncoding:NSUTF8StringEncoding]){
             data = [string dataUsingEncoding:NSUTF8StringEncoding];
-            filePath = [[NSApp delegate] temporaryFilePath:[filePath 
lastPathComponent] createDirectory:NO];
+            filePath = [[NSFileManager defaultManager] 
temporaryFileWithBasename:[filePath lastPathComponent]];
             [data writeToFile:filePath atomically:YES];
             [string release];
         }else{

Modified: trunk/bibdesk/BibDocument_DataSource.m
===================================================================
--- trunk/bibdesk/BibDocument_DataSource.m      2007-08-19 22:17:43 UTC (rev 
10948)
+++ trunk/bibdesk/BibDocument_DataSource.m      2007-08-20 05:12:19 UTC (rev 
10949)
@@ -1013,7 +1013,7 @@
             NSString *fullPath = [[dropDestination path] 
stringByAppendingPathComponent:fileName];
             
             // make sure the filename is unique
-            fullPath = [[NSFileManager defaultManager] uniqueFilePath:fullPath 
createDirectory:NO];
+            fullPath = [[NSFileManager defaultManager] 
uniqueFilePathWithName:fileName atPath:[dropDestination path]];
             return ([plist writeToFile:fullPath atomically:YES]) ? [NSArray 
arrayWithObject:fileName] : nil;
         } else
             return nil;

Modified: trunk/bibdesk/BibFiler.m
===================================================================
--- trunk/bibdesk/BibFiler.m    2007-08-19 22:17:43 UTC (rev 10948)
+++ trunk/bibdesk/BibFiler.m    2007-08-20 05:12:19 UTC (rev 10949)
@@ -374,9 +374,8 @@
     
     NSString *fileName = NSLocalizedString(@"BibDesk AutoFile Errors", 
@"Filename for dumped autofile errors.");
     NSString *path = [[NSFileManager defaultManager] desktopDirectory];
-    if (path == nil)
-        return;
-    path = [[NSFileManager defaultManager] uniqueFilePath:[[path 
stringByAppendingPathComponent:fileName] stringByAppendingPathExtension:@"txt"] 
createDirectory:NO];
+    if (path)
+        path = [[NSFileManager defaultManager] 
uniqueFilePathWithName:[fileName stringByAppendingPathExtension:@"txt"] 
atPath:path];
     
     [string writeToFile:path atomically:YES encoding:NSUTF8StringEncoding 
error:NULL];
 }
@@ -521,7 +520,7 @@
             if([self fileExistsAtPath:resolvedPath]){
                 if(force){
                     NSString *backupPath = [[self desktopDirectory] 
stringByAppendingPathComponent:[resolvedNewPath lastPathComponent]];
-                    backupPath = [self uniqueFilePath:backupPath 
createDirectory:NO];
+                    backupPath = [self uniqueFilePathWithName:[resolvedNewPath 
lastPathComponent] atPath:[self desktopDirectory]];
                     if(![self movePath:resolvedNewPath toPath:backupPath 
force:NO error:NULL] && 
                         [self fileExistsAtPath:resolvedNewPath] && 
                         ![self removeFileAtPath:resolvedNewPath handler:nil]){

Modified: trunk/bibdesk/NSFileManager_BDSKExtensions.h
===================================================================
--- trunk/bibdesk/NSFileManager_BDSKExtensions.h        2007-08-19 22:17:43 UTC 
(rev 10948)
+++ trunk/bibdesk/NSFileManager_BDSKExtensions.h        2007-08-20 05:12:19 UTC 
(rev 10949)
@@ -69,8 +69,16 @@
 - (void)createWeblocFilesInBackgroundThread:(NSDictionary *)fullPathDict;
 - (void)copyFilesInBackgroundThread:(NSDictionary *)fullPathDict;
 
-- (NSString *)uniqueFilePath:(NSString *)path createDirectory:(BOOL)create;
+// creates a temporary directory with default attributes in a system temp 
location; this is thread safe
+- (NSString *)makeTemporaryDirectoryWithBasename:(NSString *)fileName;
 
+// !!! The next two methods are not thread safe, since they return a name 
without creating a file, and other threads/processes may return the same value
+
+// accepts a filename and a directory, and returns a unique file name in that 
directory using the filename as a basename
+- (NSString *)uniqueFilePathWithName:(NSString *)fileName atPath:(NSString 
*)directory;
+// creates a file in a system temp location; pass nil for fileName if you want 
a UUID based name
+- (NSString *)temporaryFileWithBasename:(NSString *)fileName;
+
 // for spotlight stuff; thread safe
 - (BOOL)spotlightCacheFolderExists;
 - (BOOL)removeSpotlightCacheFolder;

Modified: trunk/bibdesk/NSFileManager_BDSKExtensions.m
===================================================================
--- trunk/bibdesk/NSFileManager_BDSKExtensions.m        2007-08-19 22:17:43 UTC 
(rev 10948)
+++ trunk/bibdesk/NSFileManager_BDSKExtensions.m        2007-08-20 05:12:19 UTC 
(rev 10949)
@@ -96,6 +96,54 @@
 
 @implementation NSFileManager (BDSKExtensions)
 
+static NSString *temporaryBaseDirectory = nil;
+
+// we can't use +initialize in a category, and +load is too dangerous
+__attribute__((constructor))
+static void createTemporaryDirectory()
+{    
+    // chewable items are automatically cleaned up at restart, and it's hidden 
from the user
+    FSRef chewableRef;
+    OSErr err = FSFindFolder(kUserDomain, kChewableItemsFolderType, TRUE, 
&chewableRef);
+    
+    CFAllocatorRef alloc = CFAllocatorGetDefault();
+    CFURLRef chewableURL = NULL;
+    if (noErr == err)
+        chewableURL = CFURLCreateFromFSRef(alloc, &chewableRef);
+    
+    CFStringRef baseName = CFStringCreateWithFileSystemRepresentation(alloc, 
"bibdesk");
+    CFURLRef newURL = CFURLCreateCopyAppendingPathComponent(alloc, 
chewableURL, baseName, TRUE);
+    FSRef newRef;
+    unsigned i = 1;
+    
+    // loop until CFURLGetFSRef fails, indicating we don't have a file yet
+    while (CFURLGetFSRef(newURL, &newRef)) {
+        CFRelease(baseName);
+        CFRelease(newURL);
+        baseName = CFStringCreateWithFormat(alloc, NULL, CFSTR("bibdesk-%i"), 
i++);
+        newURL = CFURLCreateCopyAppendingPathComponent(alloc, chewableURL, 
baseName, TRUE);
+    }
+    
+    if (chewableURL) CFRelease(chewableURL);
+    
+    assert(NULL != newURL);
+    
+    int nameLength = CFStringGetLength(baseName);
+    UniChar *nameBuf = CFAllocatorAllocate(alloc, nameLength * 
sizeof(UniChar), 0);
+    CFStringGetCharacters(baseName, CFRangeMake(0, nameLength), nameBuf);
+    
+    err = FSCreateDirectoryUnicode(&chewableRef, nameLength, nameBuf, 
kFSCatInfoNone, NULL, NULL, NULL, NULL);
+    CFAllocatorDeallocate(alloc, nameBuf);
+    
+    if (noErr == err)
+        temporaryBaseDirectory = (NSString *)CFURLCopyFileSystemPath(newURL, 
kCFURLPOSIXPathStyle);
+    
+    if (newURL) CFRelease(newURL);
+    if (baseName) CFRelease(baseName);
+    
+    assert(NULL != temporaryBaseDirectory);
+}
+
 - (NSString *)currentApplicationSupportPathForCurrentUser{
     
     static NSString *path = nil;
@@ -191,24 +239,56 @@
     return path;
 }
 
-- (NSString *)uniqueFilePath:(NSString *)path createDirectory:(BOOL)create{
-    @synchronized(self){
-        NSString *basePath = [path stringByDeletingPathExtension];
-        NSString *extension = [path pathExtension];
-        int i = 0;
+#pragma mark Temporary files and directories
+
+- (NSString *)temporaryFileWithBasename:(NSString *)fileName;
+{
+       if(nil == fileName)
+        fileName = [[NSProcessInfo processInfo] globallyUniqueString];
+       return [self uniqueFilePathWithName:fileName 
atPath:temporaryBaseDirectory];
+}
+
+// This method is subject to a race condition in our temporary directory if we 
pass the same baseName to this method and temporaryFileWithBasename: 
simultaneously; hence the lock in uniqueFilePathWithName:atPath:, even though 
it and temporaryFileWithBasename: are not thread safe or secure.
+- (NSString *)makeTemporaryDirectoryWithBasename:(NSString *)baseName {
+    NSParameterAssert(baseName != nil);
+    NSString *finalPath = nil;
+    
+    @synchronized(self) {
+        unsigned i = 0;
+        NSURL *fileURL = [NSURL fileURLWithPath:[temporaryBaseDirectory 
stringByAppendingPathComponent:baseName]];
+        while ([self objectExistsAtFileURL:fileURL]) {
+            fileURL = [NSURL fileURLWithPath:[temporaryBaseDirectory 
stringByAppendingPathComponent:[NSString stringWithFormat:@"[EMAIL PROTECTED]", 
baseName, ++i]]];
+        }
+        finalPath = [fileURL path];
         
-        if(![extension isEqualToString:@""])
-            extension = [@"." stringByAppendingString:extension];
-        
-        while([self fileExistsAtPath:path])
-            path = [NSString stringWithFormat:@"[EMAIL PROTECTED]@", basePath, 
++i, extension];
-        
-        if(create)
-            [self createDirectoryAtPath:path attributes:nil];
+        // raise if we can't create a file in the chewable folder?
+        if (NO == [self createDirectoryAtPathWithNoAttributes:finalPath])
+            finalPath = nil;
     }
-       return path;
+    return finalPath;
 }
 
+- (NSString *)uniqueFilePathWithName:(NSString *)fileName atPath:(NSString 
*)directory {
+    // could expand this path?
+    NSParameterAssert([directory hasPrefix:[NSString pathSeparator]]);
+    NSParameterAssert([fileName hasPrefix:[NSString pathSeparator]] == NO);
+    NSString *baseName = [fileName stringByDeletingPathExtension];
+    NSString *extension = [fileName pathExtension];
+    
+    // optimistically assume we can just return the sender's guess of 
/directory/filename
+    NSString *fullPath = [directory stringByAppendingPathComponent:fileName];
+    int i = 0;
+    
+    // this method is always invoked from the main thread, but we don't want 
multiple threads in temporaryBaseDirectory (which may be passed as directory 
here); could make the lock conditional, but performance isn't a concern here
+    @synchronized(self) {
+    // if the file exists, try /directory/filename-i.extension
+    while([self fileExistsAtPath:fullPath])
+        fullPath = [directory stringByAppendingPathComponent:[[NSString 
stringWithFormat:@"[EMAIL PROTECTED]", baseName, ++i] 
stringByAppendingPathExtension:extension]];
+    }
+
+       return fullPath;
+}
+
 // note: IC is not thread safe
 - (NSURL *)downloadFolderURL;
 {


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit

Reply via email to