Revision: 4241
          http://skim-app.svn.sourceforge.net/skim-app/?rev=4241&view=rev
Author:   hofman
Date:     2008-07-17 19:02:39 +0000 (Thu, 17 Jul 2008)

Log Message:
-----------
Add a fileManager category for the tool to consolidate read/write code of skim 
notes for several file types. Allow passing a PDF bundle instead of a PDF file 
to the tool. Add an action to convert a PDF file to a PDF bundle and vv.

Modified Paths:
--------------
    trunk/SkimNotes/NSFileManager_SKNExtensions.m
    trunk/SkimNotes/SKNAgentListener.m
    trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj
    trunk/SkimNotes/skimnotes.m

Added Paths:
-----------
    trunk/SkimNotes/NSFileManager_SKNToolExtensions.h
    trunk/SkimNotes/NSFileManager_SKNToolExtensions.m
    trunk/SkimNotes/SKNUtilities.h
    trunk/SkimNotes/SKNUtilities.m

Modified: trunk/SkimNotes/NSFileManager_SKNExtensions.m
===================================================================
--- trunk/SkimNotes/NSFileManager_SKNExtensions.m       2008-07-17 17:04:15 UTC 
(rev 4240)
+++ trunk/SkimNotes/NSFileManager_SKNExtensions.m       2008-07-17 19:02:39 UTC 
(rev 4241)
@@ -38,74 +38,10 @@
 
 #import "NSFileManager_SKNExtensions.h"
 #import "SKNExtendedAttributeManager.h"
+#import "SKNUtilities.h"
 
-#define SKIM_NOTES_KEY @"net_sourceforge_skim-app_notes"
-#define SKIM_RTF_NOTES_KEY @"net_sourceforge_skim-app_rtf_notes"
-#define SKIM_TEXT_NOTES_KEY @"net_sourceforge_skim-app_text_notes"
-
-#define NOTE_PAGE_INDEX_KEY @"pageIndex"
-#define NOTE_TYPE_KEY @"type"
-#define NOTE_CONTENTS_KEY @"contents"
-#define NOTE_TEXT_KEY @"text"
-
 @implementation NSFileManager (SKNExtensions)
 
-static NSString *SKNTextNotes(NSArray *noteDicts) {
-    NSMutableString *textString = [NSMutableString string];
-    NSEnumerator *dictEnum = [noteDicts objectEnumerator];
-    NSDictionary *dict;
-    
-    while (dict = [dictEnum nextObject]) {
-        NSString *type = [dict objectForKey:NOTE_TYPE_KEY];
-        NSUInteger pageIndex = [[dict objectForKey:NOTE_PAGE_INDEX_KEY] 
unsignedIntValue];
-        NSString *string = [dict objectForKey:NOTE_CONTENTS_KEY];
-        NSAttributedString *text = [dict objectForKey:NOTE_TEXT_KEY];
-        
-        if (pageIndex == NSNotFound || pageIndex == UINT_MAX)
-            pageIndex = 0;
-        
-        [textString appendFormat:@"* %@, page %lu\n\n", type, (long)pageIndex 
+ 1];
-        if ([string length]) {
-            [textString appendString:string];
-            [textString appendString:@" \n\n"];
-        }
-        if ([text length]) {
-            [textString appendString:[text string]];
-            [textString appendString:@" \n\n"];
-        }
-    }
-    return textString;
-}
-
-static NSAttributedString *SKNRichTextNotes(NSArray *noteDicts) {
-    NSMutableAttributedString *attrString = [[[NSMutableAttributedString 
alloc] init] autorelease];
-    NSEnumerator *dictEnum = [noteDicts objectEnumerator];
-    NSDictionary *dict;
-    
-    while (dict = [dictEnum nextObject]) {
-        NSString *type = [dict objectForKey:NOTE_TYPE_KEY];
-        NSUInteger pageIndex = [[dict objectForKey:NOTE_PAGE_INDEX_KEY] 
unsignedIntValue];
-        NSString *string = [dict objectForKey:NOTE_CONTENTS_KEY];
-        NSAttributedString *text = [dict objectForKey:NOTE_TEXT_KEY];
-        
-        if (pageIndex == NSNotFound || pageIndex == UINT_MAX)
-            pageIndex = 0;
-        
-        [attrString replaceCharactersInRange:NSMakeRange([attrString length], 
0) withString:[NSString stringWithFormat:@"* %@, page %lu\n\n", type, 
(long)pageIndex + 1]];
-        if ([string length]) {
-            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:string];
-            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:@" \n\n"];
-        }
-        if ([text length]) {
-            [attrString appendAttributedString:text];
-            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:@" \n\n"];
-            
-        }
-    }
-    [attrString fixAttributesInRange:NSMakeRange(0, [attrString length])];
-    return attrString;
-}
-
 - (BOOL)writeSkimNotes:(NSArray *)notes toExtendedAttributesAtURL:(NSURL 
*)aURL error:(NSError **)outError {
     return [self writeSkimNotes:notes textNotes:nil richTextNotes:nil 
toExtendedAttributesAtURL:aURL error:outError];
 }
@@ -135,10 +71,8 @@
             } else {
                 if (notesString == nil)
                     notesString = SKNTextNotes(notes);
-                if (notesRTFData == nil) {
-                    NSAttributedString *notesAttrString = 
SKNRichTextNotes(notes);
-                    notesRTFData = [notesAttrString 
RTFFromRange:NSMakeRange(0, [notesAttrString length]) documentAttributes:nil];
-                }
+                if (notesRTFData == nil)
+                    notesRTFData = SKNRTFNotes(notes);
                 [eam setExtendedAttributeNamed:SKIM_TEXT_NOTES_KEY 
toPropertyListValue:notesString atPath:path options:0 error:NULL];
                 [eam setExtendedAttributeNamed:SKIM_RTF_NOTES_KEY 
toValue:notesRTFData atPath:path options:0 error:NULL];
             }

Added: trunk/SkimNotes/NSFileManager_SKNToolExtensions.h
===================================================================
--- trunk/SkimNotes/NSFileManager_SKNToolExtensions.h                           
(rev 0)
+++ trunk/SkimNotes/NSFileManager_SKNToolExtensions.h   2008-07-17 19:02:39 UTC 
(rev 4241)
@@ -0,0 +1,52 @@
+//
+//  NSFileManager_SKNToolExtensions.h
+//  SkimNotes
+//
+//  Created by Christiaan Hofman on 7/17/08.
+/*
+ This software is Copyright (c) 2008
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
[EMAIL PROTECTED] NSFileManager (SKNToolExtensions)
+
+- (NSData *)SkimNotesAtPath:(NSString *)path error:(NSError **)outError;
+- (NSString *)SkimTextNotesAtPath:(NSString *)path error:(NSError **)outError;
+- (NSData *)SkimRTFNotesAtPath:(NSString *)path error:(NSError **)outError;
+
+- (BOOL)writeSkimNotes:(NSData *)notesData textNotes:(NSString *)textNotes 
RTFNotes:(NSData *)rtfNotesData atPath:(NSString *)path error:(NSError 
**)outError;
+
+- (BOOL)removeSkimNotesAtPath:(NSString *)path error:(NSError **)outError;
+
[EMAIL PROTECTED]

Added: trunk/SkimNotes/NSFileManager_SKNToolExtensions.m
===================================================================
--- trunk/SkimNotes/NSFileManager_SKNToolExtensions.m                           
(rev 0)
+++ trunk/SkimNotes/NSFileManager_SKNToolExtensions.m   2008-07-17 19:02:39 UTC 
(rev 4241)
@@ -0,0 +1,231 @@
+//
+//  NSFileManager_SKNToolExtensions.m
+//  SkimNotes
+//
+//  Created by Christiaan Hofman on 7/17/08.
+/*
+ This software is Copyright (c) 2008
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "NSFileManager_SKNToolExtensions.h"
+#import "SKNExtendedAttributeManager.h"
+#import "SKNUtilities.h"
+
+#define PDFD_EXTENSION  @"pdfd"
+#define SKIM_EXTENSION  @"skim"
+#define TXT_EXTENSION   @"txt"
+#define RTF_EXTENSION   @"rtf"
+
+#define BUNDLE_DATA_FILENAME @"data"
+
[EMAIL PROTECTED] NSFileManager (SKNToolExtensions)
+
+- (NSString *)notesFileWithExtension:(NSString *)extension atPath:(NSString 
*)path error:(NSError **)error {
+    NSString *filePath = nil;
+    
+    path = [path stringByStandardizingPath];
+    if ([extension caseInsensitiveCompare:SKIM_EXTENSION] == NSOrderedSame) {
+        NSArray *files = [self subpathsAtPath:path];
+        NSString *filename = [[[path lastPathComponent] 
stringByDeletingPathExtension] stringByAppendingPathExtension:SKIM_EXTENSION];
+        if ([files containsObject:filename] == NO) {
+            NSUInteger idx = [[files 
valueForKeyPath:@"pathExtension.lowercaseString"] indexOfObject:SKIM_EXTENSION];
+            filename = idx == NSNotFound ? nil : [files objectAtIndex:idx];
+        }
+        if (filename)
+            filePath = [path stringByAppendingPathComponent:filename];
+    } else {
+        NSString *skimFile = [self notesFileWithExtension:SKIM_EXTENSION 
atPath:path error:error];
+        if (skimFile) {
+            filePath = [[skimFile stringByDeletingPathExtension] 
stringByAppendingPathExtension:extension];
+            if ([[NSFileManager defaultManager] fileExistsAtPath:filePath] == 
NO)
+                filePath = nil;
+        }
+    }
+    if (filePath == nil && error) 
+        *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOENT 
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Notes file note found", 
NSLocalizedDescriptionKey, nil]];
+    return filePath;
+}
+
+- (NSData *)SkimNotesAtPath:(NSString *)path error:(NSError **)outError {
+    NSError *error;
+    NSData *data = nil;
+    NSString *extension = [path pathExtension];
+    
+    path = [path stringByStandardizingPath];
+    if ([extension caseInsensitiveCompare:PDFD_EXTENSION] == NSOrderedSame) {
+        NSString *notePath = [self notesFileWithExtension:SKIM_EXTENSION 
atPath:path error:&error];
+        if (notePath)
+            data = [NSData dataWithContentsOfFile:notePath options:0 
error:&error];
+        if (nil == data && outError)
+            *outError = error;
+    } else if ([extension caseInsensitiveCompare:SKIM_EXTENSION] == 
NSOrderedSame) {
+        data = [NSData dataWithContentsOfFile:path options:0 error:&error];
+        if (nil == data && outError)
+            *outError = error;
+    } else {
+        data = [[SKNExtendedAttributeManager sharedManager] 
extendedAttributeNamed:SKIM_NOTES_KEY atPath:path traverseLink:YES 
error:&error];
+        if (nil == data) {
+            if ([error code] != ENOATTR)
+                data = [NSData data];
+            else if (outError)
+                *outError = error;
+        }
+    }
+    return data;
+}
+
+- (NSString *)SkimTextNotesAtPath:(NSString *)path error:(NSError **)outError {
+    NSError *error;
+    NSString *string = nil;
+    NSString *extension = [path pathExtension];
+    
+    path = [path stringByStandardizingPath];
+    if ([extension caseInsensitiveCompare:PDFD_EXTENSION] == NSOrderedSame) {
+        NSString *notePath = [self notesFileWithExtension:TXT_EXTENSION 
atPath:path error:&error];
+        if (notePath)
+            string = [NSString stringWithContentsOfFile:notePath 
encoding:NSUTF8StringEncoding error:&error];
+        if (nil == string && outError)
+            *outError = error;
+    } else {
+        string = [[SKNExtendedAttributeManager sharedManager] 
propertyListFromExtendedAttributeNamed:SKIM_TEXT_NOTES_KEY atPath:path 
traverseLink:YES error:&error];
+        if (nil == string) {
+            if ([error code] != ENOATTR)
+                string = @"";
+            else if (outError)
+                *outError = error;
+        }
+    }
+    return string;
+}
+
+- (NSData *)SkimRTFNotesAtPath:(NSString *)path error:(NSError **)outError {
+    NSError *error;
+    NSData *data = nil;
+    NSString *extension = [path pathExtension];
+    
+    path = [path stringByStandardizingPath];
+    if ([extension caseInsensitiveCompare:PDFD_EXTENSION] == NSOrderedSame) {
+        NSString *notePath = [self notesFileWithExtension:RTF_EXTENSION 
atPath:path error:&error];
+        if (notePath)
+            data = [NSData dataWithContentsOfFile:notePath options:0 
error:&error];
+        if (nil == data && outError)
+            *outError = error;
+    } else {
+        data = [[SKNExtendedAttributeManager sharedManager] 
extendedAttributeNamed:SKIM_RTF_NOTES_KEY atPath:path traverseLink:YES 
error:&error];
+        if (nil == data) {
+            if ([error code] != ENOATTR)
+                data = [NSData data];
+            else if (outError)
+                *outError = error;
+        }
+    }
+    return data;
+}
+
+- (BOOL)writeSkimNotes:(NSData *)notesData textNotes:(NSString *)textNotes 
RTFNotes:(NSData *)rtfNotesData atPath:(NSString *)path error:(NSError 
**)outError {
+    BOOL success = YES;
+    NSError *error = nil;
+    NSString *extension = [path pathExtension];
+    
+    [self removeSkimNotesAtPath:path error:NULL];
+    if ([notesData length]) {
+        NSArray *notes = [NSKeyedUnarchiver unarchiveObjectWithData:notesData];
+        if ([notes count]) {
+            if (textNotes == nil)
+                textNotes = SKNTextNotes(notes);
+            if (rtfNotesData == nil)
+                rtfNotesData = SKNRTFNotes(notes);
+        }
+        NSData *textNotesData = [textNotes 
dataUsingEncoding:NSUTF8StringEncoding];
+        if ([extension caseInsensitiveCompare:PDFD_EXTENSION] == 
NSOrderedSame) {
+            NSString *name = [[path lastPathComponent] 
stringByDeletingPathExtension];
+            if ([name caseInsensitiveCompare:BUNDLE_DATA_FILENAME] == 
NSOrderedSame)
+                name = [name stringByAppendingString:@"1"];
+            NSString *notePath = [[path stringByAppendingPathComponent:name] 
stringByAppendingPathExtension:SKIM_EXTENSION];
+            success = [notesData writeToFile:notePath options:0 error:&error];
+            if (textNotesData) {
+                notePath = [[path stringByAppendingPathComponent:name] 
stringByAppendingPathExtension:TXT_EXTENSION];
+                [textNotesData writeToFile:notePath options:0 error:NULL];
+            }
+            if (rtfNotesData) {
+                notePath = [[path stringByAppendingPathComponent:name] 
stringByAppendingPathExtension:TXT_EXTENSION];
+                [rtfNotesData writeToFile:notePath options:0 error:NULL];
+            }
+        } else {
+            SKNExtendedAttributeManager *eam = [SKNExtendedAttributeManager 
sharedManager];
+            success = [eam setExtendedAttributeNamed:SKIM_NOTES_KEY 
toValue:notesData atPath:path options:0 error:&error];
+            if (textNotesData)
+                [eam setExtendedAttributeNamed:SKIM_TEXT_NOTES_KEY 
toValue:textNotesData atPath:path options:0 error:NULL];
+            if (rtfNotesData)
+                [eam setExtendedAttributeNamed:SKIM_RTF_NOTES_KEY 
toValue:rtfNotesData atPath:path options:0 error:NULL];
+        }
+    }
+    return success;
+}
+
+- (BOOL)removeSkimNotesAtPath:(NSString *)path error:(NSError **)outError {
+    BOOL success1 = YES, success2 = YES, success3 = YES;
+    NSError *error1 = nil, *error2 = nil, *error3 = nil;
+    NSString *extension = [path pathExtension];
+    
+    if ([extension caseInsensitiveCompare:PDFD_EXTENSION] == NSOrderedSame) {
+        NSString *notePath;
+        if (notePath = [self notesFileWithExtension:SKIM_EXTENSION atPath:path 
error:NULL])
+            success1 = [self removeFileAtPath:notePath handler:nil];
+        if (notePath = [self notesFileWithExtension:TXT_EXTENSION atPath:path 
error:NULL])
+            success2 = [self removeFileAtPath:notePath handler:nil];
+        if (notePath = [self notesFileWithExtension:RTF_EXTENSION atPath:path 
error:NULL])
+            success3 = [self removeFileAtPath:notePath handler:nil];
+        if (success1 == NO || success2 == NO || success3 == NO)
+            error1 = error2 = error3 = [NSError 
errorWithDomain:NSPOSIXErrorDomain code:ENOENT userInfo:[NSDictionary 
dictionaryWithObjectsAndKeys:@"Could not remove notes file", 
NSLocalizedDescriptionKey, nil]];
+    } else {
+        SKNExtendedAttributeManager *eam = [SKNExtendedAttributeManager 
sharedManager];
+        success1 = [eam removeExtendedAttribute:SKIM_NOTES_KEY atPath:path 
traverseLink:YES error:&error1];
+        if (success1 == NO && [error1 code] == ENOATTR)
+            success1 = YES;
+        success2 = [eam removeExtendedAttribute:SKIM_RTF_NOTES_KEY atPath:path 
traverseLink:YES error:&error2];
+        if (success2 == NO && [error2 code] == ENOATTR)
+            success2 = YES;
+        success3 = [eam removeExtendedAttribute:SKIM_TEXT_NOTES_KEY 
atPath:path traverseLink:YES error:&error3];
+        if (success3 == NO && [error3 code] == ENOATTR)
+            success3 = YES;
+    }
+    if (success1 == NO && outError)
+        *outError = error1;
+    else if (success2 == NO && outError)
+        *outError = error2;
+    else if (success3 == NO && outError)
+        *outError = error3;
+    return success1 && success2 && success3;
+}
+
[EMAIL PROTECTED]

Modified: trunk/SkimNotes/SKNAgentListener.m
===================================================================
--- trunk/SkimNotes/SKNAgentListener.m  2008-07-17 17:04:15 UTC (rev 4240)
+++ trunk/SkimNotes/SKNAgentListener.m  2008-07-17 19:02:39 UTC (rev 4241)
@@ -38,12 +38,8 @@
 #import "SKNAgentListener.h"
 #import "SKNAgentListenerProtocol.h"
 #import <AppKit/AppKit.h>
-#import "SKNExtendedAttributeManager.h"
+#import "NSFileManager_SKNToolExtensions.h"
 
-#define SKIM_NOTES_KEY @"net_sourceforge_skim-app_notes"
-#define SKIM_RTF_NOTES_KEY @"net_sourceforge_skim-app_rtf_notes"
-#define SKIM_TEXT_NOTES_KEY @"net_sourceforge_skim-app_text_notes"
-
 @implementation SKNAgentListener
 
 - (id)initWithServerName:(NSString *)serverName;
@@ -79,92 +75,30 @@
     [super dealloc];
 }
 
-- (NSString *)notesFileWithExtension:(NSString *)extension atPath:(NSString 
*)path error:(NSError **)error {
-    NSString *filePath = nil;
-    
-    if ([extension caseInsensitiveCompare:@"skim"] == NSOrderedSame) {
-        NSArray *files = [[NSFileManager defaultManager] subpathsAtPath:path];
-        NSString *filename = [[[path lastPathComponent] 
stringByDeletingPathExtension] stringByAppendingPathExtension:@"skim"];
-        if ([files containsObject:filename] == NO) {
-            NSUInteger idx = [[files 
valueForKeyPath:@"pathExtension.lowercaseString"] indexOfObject:@"skim"];
-            filename = idx == NSNotFound ? nil : [files objectAtIndex:idx];
-        }
-        if (filename)
-            filePath = [path stringByAppendingPathComponent:filename];
-    } else {
-        NSString *skimFile = [self notesFileWithExtension:@"skim" atPath:path 
error:error];
-        if (skimFile) {
-            filePath = [[skimFile stringByDeletingPathExtension] 
stringByAppendingPathExtension:extension];
-            if ([[NSFileManager defaultManager] fileExistsAtPath:filePath] == 
NO)
-                filePath = nil;
-        }
-    }
-    if (filePath == nil && error) 
-        *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOENT 
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Notes file note found", 
NSLocalizedDescriptionKey, nil]];
-    return filePath;
-}
-
 - (bycopy NSData *)SkimNotesAtPath:(in bycopy NSString *)aFile;
 {
-    NSError *error;
-    NSData *data = nil;
-    NSString *extension = [[aFile pathExtension] lastPathComponent];
-    
-    if ([extension caseInsensitiveCompare:@"pdfd"] == NSOrderedSame) {
-        NSString *notePath = [self notesFileWithExtension:@"skim" atPath:aFile 
error:&error];
-        if (notePath)
-            data = [NSData dataWithContentsOfFile:notePath options:0 
error:&error];
-        if (nil == data)
-            fprintf(stderr, "skimnotes agent pid %d: error getting Skim notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    } else if ([extension caseInsensitiveCompare:@"skim"] == NSOrderedSame) {
-        data = [NSData dataWithContentsOfFile:aFile options:0 error:&error];
-        if (nil == data)
-            fprintf(stderr, "skimnotes agent pid %d: error getting Skim notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    } else {
-        data = [[SKNExtendedAttributeManager sharedManager] 
extendedAttributeNamed:SKIM_NOTES_KEY atPath:[aFile stringByStandardizingPath] 
traverseLink:YES error:&error];
-        if (nil == data && [error code] != ENOATTR)
-            fprintf(stderr, "skimnotes agent pid %d: error getting Skim notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    }
+    NSError *error = nil;
+    NSData *data = [[NSFileManager defaultManager] SkimNotesAtPath:aFile 
error:&error];
+    if (nil == data)
+        fprintf(stderr, "skimnotes agent pid %d: error getting Skim notes 
(%s)\n", getpid(), [[error description] UTF8String]);
     return data;
 }
 
 - (bycopy NSData *)RTFNotesAtPath:(in bycopy NSString *)aFile;
 {
-    NSError *error;
-    NSData *data = nil;
-    NSString *extension = [[aFile pathExtension] lastPathComponent];
-    
-    if ([extension caseInsensitiveCompare:@"pdfd"] == NSOrderedSame) {
-        NSString *notePath = [self notesFileWithExtension:@"rtf" atPath:aFile 
error:&error];
-        if (notePath)
-            data = [NSData dataWithContentsOfFile:notePath options:0 
error:&error];
-        if (nil == data)
-            fprintf(stderr, "skimnotes agent pid %d: error getting RTF notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    } else {
-        data = [[SKNExtendedAttributeManager sharedManager] 
extendedAttributeNamed:SKIM_RTF_NOTES_KEY atPath:[aFile 
stringByStandardizingPath] traverseLink:YES error:&error];
-        if (nil == data && [error code] != ENOATTR)
-            fprintf(stderr, "skimnotes agent pid %d: error getting RTF notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    }
+    NSError *error = nil;
+    NSData *data = [[NSFileManager defaultManager] SkimRTFNotesAtPath:aFile 
error:&error];
+    if (nil == data)
+        fprintf(stderr, "skimnotes agent pid %d: error getting RTF notes 
(%s)\n", getpid(), [[error description] UTF8String]);
     return data;
 }
 
 - (bycopy NSData *)textNotesAtPath:(in bycopy NSString *)aFile 
encoding:(NSStringEncoding)encoding;
 {
-    NSError *error;
-    NSString *string = nil;
-    NSString *extension = [[aFile pathExtension] lastPathComponent];
-    
-    if ([extension caseInsensitiveCompare:@"pdfd"] == NSOrderedSame) {
-        NSString *notePath = [self notesFileWithExtension:@"txt" atPath:aFile 
error:&error];
-        if (notePath)
-            string = [NSString stringWithContentsOfFile:notePath 
encoding:NSUTF8StringEncoding error:&error];
-        if (nil == string)
-            fprintf(stderr, "skimnotes agent pid %d: error getting text notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    } else {
-        string = [[SKNExtendedAttributeManager sharedManager] 
propertyListFromExtendedAttributeNamed:SKIM_TEXT_NOTES_KEY atPath:[aFile 
stringByStandardizingPath] traverseLink:YES error:&error];
-        if (nil == string && [error code] != ENOATTR)
-            fprintf(stderr, "skimnotes agent pid %d: error getting text notes 
(%s)\n", getpid(), [[error description] UTF8String]);
-    }
+    NSError *error = nil;
+    NSString *string = [[NSFileManager defaultManager] 
SkimTextNotesAtPath:aFile error:&error];
+    if (nil == string)
+        fprintf(stderr, "skimnotes agent pid %d: error getting text notes 
(%s)\n", getpid(), [[error description] UTF8String]);
     // Returning the string directly can fail under some conditions.  For some 
strings with corrupt copy-paste characters (typical for notes), -[NSString 
canBeConvertedToEncoding:NSUTF8StringEncoding] returns YES but the actual 
conversion fails.  A result seems to be that encoding the string also fails, 
which causes the DO client to get a timeout.  Returning NSUnicodeStringEncoding 
data seems to work in those cases (and is safe since we're not going over the 
wire between big/little-endian systems).
     return [string dataUsingEncoding:encoding];
 }

Added: trunk/SkimNotes/SKNUtilities.h
===================================================================
--- trunk/SkimNotes/SKNUtilities.h                              (rev 0)
+++ trunk/SkimNotes/SKNUtilities.h      2008-07-17 19:02:39 UTC (rev 4241)
@@ -0,0 +1,46 @@
+//
+//  SKNUtilities.h
+//  SkimNotes
+//
+//  Created by Christiaan Hofman on 7/17/08.
+/*
+ This software is Copyright (c) 2008
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#define SKIM_NOTES_KEY      @"net_sourceforge_skim-app_notes"
+#define SKIM_RTF_NOTES_KEY  @"net_sourceforge_skim-app_rtf_notes"
+#define SKIM_TEXT_NOTES_KEY @"net_sourceforge_skim-app_text_notes"
+
+extern NSString *SKNTextNotes(NSArray *noteDicts);
+extern NSData *SKNRTFNotes(NSArray *noteDicts);

Added: trunk/SkimNotes/SKNUtilities.m
===================================================================
--- trunk/SkimNotes/SKNUtilities.m                              (rev 0)
+++ trunk/SkimNotes/SKNUtilities.m      2008-07-17 19:02:39 UTC (rev 4241)
@@ -0,0 +1,100 @@
+//
+//  SKNUtilities.m
+//  SkimNotes
+//
+//  Created by Christiaan Hofman on 7/17/08.
+/*
+ This software is Copyright (c) 2008
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "SKNUtilities.h"
+
+#define NOTE_PAGE_INDEX_KEY @"pageIndex"
+#define NOTE_TYPE_KEY @"type"
+#define NOTE_CONTENTS_KEY @"contents"
+#define NOTE_TEXT_KEY @"text"
+
+NSString *SKNTextNotes(NSArray *noteDicts) {
+    NSMutableString *textString = [NSMutableString string];
+    NSEnumerator *dictEnum = [noteDicts objectEnumerator];
+    NSDictionary *dict;
+    
+    while (dict = [dictEnum nextObject]) {
+        NSString *type = [dict objectForKey:NOTE_TYPE_KEY];
+        NSUInteger pageIndex = [[dict objectForKey:NOTE_PAGE_INDEX_KEY] 
unsignedIntValue];
+        NSString *string = [dict objectForKey:NOTE_CONTENTS_KEY];
+        NSAttributedString *text = [dict objectForKey:NOTE_TEXT_KEY];
+        
+        if (pageIndex == NSNotFound || pageIndex == UINT_MAX)
+            pageIndex = 0;
+        
+        [textString appendFormat:@"* %@, page %lu\n\n", type, (long)pageIndex 
+ 1];
+        if ([string length]) {
+            [textString appendString:string];
+            [textString appendString:@" \n\n"];
+        }
+        if ([text length]) {
+            [textString appendString:[text string]];
+            [textString appendString:@" \n\n"];
+        }
+    }
+    return textString;
+}
+
+NSData *SKNRTFNotes(NSArray *noteDicts) {
+    NSMutableAttributedString *attrString = [[[NSMutableAttributedString 
alloc] init] autorelease];
+    NSEnumerator *dictEnum = [noteDicts objectEnumerator];
+    NSDictionary *dict;
+    
+    while (dict = [dictEnum nextObject]) {
+        NSString *type = [dict objectForKey:NOTE_TYPE_KEY];
+        NSUInteger pageIndex = [[dict objectForKey:NOTE_PAGE_INDEX_KEY] 
unsignedIntValue];
+        NSString *string = [dict objectForKey:NOTE_CONTENTS_KEY];
+        NSAttributedString *text = [dict objectForKey:NOTE_TEXT_KEY];
+        
+        if (pageIndex == NSNotFound || pageIndex == UINT_MAX)
+            pageIndex = 0;
+        
+        [attrString replaceCharactersInRange:NSMakeRange([attrString length], 
0) withString:[NSString stringWithFormat:@"* %@, page %lu\n\n", type, 
(long)pageIndex + 1]];
+        if ([string length]) {
+            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:string];
+            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:@" \n\n"];
+        }
+        if ([text length]) {
+            [attrString appendAttributedString:text];
+            [attrString replaceCharactersInRange:NSMakeRange([attrString 
length], 0) withString:@" \n\n"];
+            
+        }
+    }
+    [attrString fixAttributesInRange:NSMakeRange(0, [attrString length])];
+    return [attrString RTFFromRange:NSMakeRange(0, [attrString length]) 
documentAttributes:nil];
+}

Modified: trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj
===================================================================
--- trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2008-07-17 17:04:15 UTC 
(rev 4240)
+++ trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2008-07-17 19:02:39 UTC 
(rev 4241)
@@ -37,6 +37,12 @@
 
 /* Begin PBXBuildFile section */
                8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; 
};
+               CE37766F0E2FBB7300261604 /* NSFileManager_SKNToolExtensions.m 
in Sources */ = {isa = PBXBuildFile; fileRef = CE3776640E2FB47500261604 /* 
NSFileManager_SKNToolExtensions.m */; };
+               CE37768C0E2FC26100261604 /* SKNUtilities.h in Headers */ = {isa 
= PBXBuildFile; fileRef = CE37768A0E2FC26100261604 /* SKNUtilities.h */; };
+               CE37768D0E2FC26100261604 /* SKNUtilities.m in Sources */ = {isa 
= PBXBuildFile; fileRef = CE37768B0E2FC26100261604 /* SKNUtilities.m */; };
+               CE3776950E2FC51900261604 /* SKNUtilities.m in Sources */ = {isa 
= PBXBuildFile; fileRef = CE37768B0E2FC26100261604 /* SKNUtilities.m */; };
+               CE3776960E2FC52A00261604 /* SKNUtilities.m in Sources */ = {isa 
= PBXBuildFile; fileRef = CE37768B0E2FC26100261604 /* SKNUtilities.m */; };
+               CE3776970E2FC53000261604 /* SKNUtilities.h in Headers */ = {isa 
= PBXBuildFile; fileRef = CE37768A0E2FC26100261604 /* SKNUtilities.h */; };
                CE3907B00E082C460015B0B7 /* SkimNotes.strings in Resources */ = 
{isa = PBXBuildFile; fileRef = CE3907AF0E082C460015B0B7 /* SkimNotes.strings 
*/; };
                CEA5F54E0E2CEDFB00F65088 /* SkimNotesBase.h in Headers */ = 
{isa = PBXBuildFile; fileRef = CEA5F5490E2CED6D00F65088 /* SkimNotesBase.h */; 
settings = {ATTRIBUTES = (Public, ); }; };
                CEA5F54F0E2CEDFE00F65088 /* NSFileManager_SKNExtensions.h in 
Headers */ = {isa = PBXBuildFile; fileRef = CEBA2BD30E05826D0000B2E6 /* 
NSFileManager_SKNExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -147,6 +153,10 @@
                CE17EF880E250E8A00DE06EA /* SkimNotes-Release.xcconfig */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; 
name = "SkimNotes-Release.xcconfig"; path = 
"Configurations/SkimNotes-Release.xcconfig"; sourceTree = "<group>"; };
                CE17EF890E250E8A00DE06EA /* SkimNotes-Tool.xcconfig */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = 
"SkimNotes-Tool.xcconfig"; path = "Configurations/SkimNotes-Tool.xcconfig"; 
sourceTree = "<group>"; };
                CE17F1BB0E25861700DE06EA /* SkimNotes_Tool_Prefix.pch */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path 
= SkimNotes_Tool_Prefix.pch; sourceTree = "<group>"; };
+               CE3776630E2FB47500261604 /* NSFileManager_SKNToolExtensions.h 
*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.h; path = NSFileManager_SKNToolExtensions.h; sourceTree = 
"<group>"; };
+               CE3776640E2FB47500261604 /* NSFileManager_SKNToolExtensions.m 
*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.objc; path = NSFileManager_SKNToolExtensions.m; sourceTree = 
"<group>"; };
+               CE37768A0E2FC26100261604 /* SKNUtilities.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
SKNUtilities.h; sourceTree = "<group>"; };
+               CE37768B0E2FC26100261604 /* SKNUtilities.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= SKNUtilities.m; sourceTree = "<group>"; };
                CE3907AD0E082C2D0015B0B7 /* English */ = {isa = 
PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; 
name = English; path = English.lproj/SkimNotes.strings; sourceTree = "<group>"; 
};
                CE3907B40E082C900015B0B7 /* Dutch */ = {isa = PBXFileReference; 
lastKnownFileType = text.plist.strings; name = Dutch; path = 
Dutch.lproj/SkimNotes.strings; sourceTree = "<group>"; };
                CE3907B50E082C930015B0B7 /* French */ = {isa = 
PBXFileReference; lastKnownFileType = text.plist.strings; name = French; path = 
French.lproj/SkimNotes.strings; sourceTree = "<group>"; };
@@ -334,10 +344,12 @@
                CEBA2B610E0566E60000B2E6 /* Tool Classes */ = {
                        isa = PBXGroup;
                        children = (
+                               CEBA2B5D0E0566DF0000B2E6 /* skimnotes.m */,
+                               CE3776630E2FB47500261604 /* 
NSFileManager_SKNToolExtensions.h */,
+                               CE3776640E2FB47500261604 /* 
NSFileManager_SKNToolExtensions.m */,
                                CEBA2B5A0E0566DF0000B2E6 /* SKNAgentListener.h 
*/,
                                CEBA2B5B0E0566DF0000B2E6 /* SKNAgentListener.m 
*/,
                                CEBA2B5C0E0566DF0000B2E6 /* 
SKNAgentListenerProtocol.h */,
-                               CEBA2B5D0E0566DF0000B2E6 /* skimnotes.m */,
                        );
                        name = "Tool Classes";
                        sourceTree = "<group>";
@@ -347,6 +359,8 @@
                        children = (
                                CEBA2B630E05675E0000B2E6 /* 
SKNExtendedAttributeManager.h */,
                                CEBA2B640E05675E0000B2E6 /* 
SKNExtendedAttributeManager.m */,
+                               CE37768A0E2FC26100261604 /* SKNUtilities.h */,
+                               CE37768B0E2FC26100261604 /* SKNUtilities.m */,
                        );
                        name = "Shared Classes";
                        sourceTree = "<group>";
@@ -400,6 +414,7 @@
                                CEBA2BD50E05826D0000B2E6 /* 
NSFileManager_SKNExtensions.h in Headers */,
                                CEBA2BE20E0587EB0000B2E6 /* 
PDFDocument_SKNExtensions.h in Headers */,
                                CEBA2CD70E058CF00000B2E6 /* SkimNotes.h in 
Headers */,
+                               CE37768C0E2FC26100261604 /* SKNUtilities.h in 
Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -410,6 +425,7 @@
                                CEA5F54E0E2CEDFB00F65088 /* SkimNotesBase.h in 
Headers */,
                                CEA5F54F0E2CEDFE00F65088 /* 
NSFileManager_SKNExtensions.h in Headers */,
                                CEA5F5510E2CEE0200F65088 /* 
SKNExtendedAttributeManager.h in Headers */,
+                               CE3776970E2FC53000261604 /* SKNUtilities.h in 
Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -598,6 +614,7 @@
                                CEBA2B8E0E0569010000B2E6 /* 
SKNPDFAnnotationNote.m in Sources */,
                                CEBA2BD60E05826D0000B2E6 /* 
NSFileManager_SKNExtensions.m in Sources */,
                                CEBA2BE30E0587EB0000B2E6 /* 
PDFDocument_SKNExtensions.m in Sources */,
+                               CE37768D0E2FC26100261604 /* SKNUtilities.m in 
Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -607,6 +624,7 @@
                        files = (
                                CEA5F5500E2CEDFF00F65088 /* 
NSFileManager_SKNExtensions.m in Sources */,
                                CEA5F5520E2CEE0300F65088 /* 
SKNExtendedAttributeManager.m in Sources */,
+                               CE3776960E2FC52A00261604 /* SKNUtilities.m in 
Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -617,6 +635,8 @@
                                CEBA2B5E0E0566DF0000B2E6 /* SKNAgentListener.m 
in Sources */,
                                CEBA2B5F0E0566DF0000B2E6 /* skimnotes.m in 
Sources */,
                                CEBA2B650E05675E0000B2E6 /* 
SKNExtendedAttributeManager.m in Sources */,
+                               CE37766F0E2FBB7300261604 /* 
NSFileManager_SKNToolExtensions.m in Sources */,
+                               CE3776950E2FC51900261604 /* SKNUtilities.m in 
Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };

Modified: trunk/SkimNotes/skimnotes.m
===================================================================
--- trunk/SkimNotes/skimnotes.m 2008-07-17 17:04:15 UTC (rev 4240)
+++ trunk/SkimNotes/skimnotes.m 2008-07-17 19:02:39 UTC (rev 4241)
@@ -1,17 +1,14 @@
 #import <Foundation/Foundation.h>
-#import "SKNExtendedAttributeManager.h"
+#import "NSFileManager_SKNToolExtensions.h"
 #import "SKNAgentListener.h"
 
-#define SKIM_NOTES_KEY      @"net_sourceforge_skim-app_notes"
-#define SKIM_RTF_NOTES_KEY  @"net_sourceforge_skim-app_rtf_notes"
-#define SKIM_TEXT_NOTES_KEY @"net_sourceforge_skim-app_text_notes"
-
-static char *usageStr = "Usage:\n skimnotes get [-format skim|text|rtf] 
PDF_FILE [SKIM_FILE|RTF_FILE|TEXT_FILE|-]\n skimnotes set PDF_FILE 
[SKIM_FILE|-]\n skimnotes remove PDF_FILE\n skimnotes agent [SERVER_NAME]\n 
skimnotes protocol\n skimnotes help [VERB]\n skimnotes version";
+static char *usageStr = "Usage:\n skimnotes get [-format skim|text|rtf] 
PDF_FILE [SKIM_FILE|RTF_FILE|TEXT_FILE|-]\n skimnotes set PDF_FILE 
[SKIM_FILE|-]\n skimnotes remove PDF_FILE\n skimnotes convert 
PDF_FILE|PDF_BUNDLE [PDF_BUNDLE|PDF_FILE]\n skimnotes agent [SERVER_NAME]\n 
skimnotes protocol\n skimnotes help [VERB]\n skimnotes version";
 static char *versionStr = "SkimNotes command-line client, version 2.2.2";
 
+static char *getHelpStr = "skimnotes get: read Skim notes from a PDF\nUsage: 
skimnotes get [-format skim|text|rtf] PDF_FILE [NOTES_FILE|-]\n\nReads Skim, 
Text, or RTF notes from extended attributes of PDF_FILE and writes to 
NOTES_FILE or standard output.\nUses notes file with same base name as PDF_FILE 
if SKIM_FILE is not provided.\nReads Skim notes when no format is provided.";
 static char *setHelpStr = "skimnotes set: write Skim notes to a PDF\nUsage: 
skimnotes set PDF_FILE [SKIM_FILE|-]\n\nWrites notes to extended attributes of 
PDF_FILE from SKIM_FILE or standard input.\nUses notes file with same base name 
as PDF_FILE if SKIM_FILE is not provided.";
-static char *getHelpStr = "skimnotes get: read Skim notes from a PDF\nUsage: 
skimnotes get [-format skim|text|rtf] PDF_FILE [NOTES_FILE|-]\n\nReads Skim, 
Text, or RTF notes from extended attributes of PDF_FILE and writes to 
NOTES_FILE or standard output.\nUses notes file with same base name as PDF_FILE 
if SKIM_FILE is not provided.\nReads Skim notes when no format is provided.";
 static char *removeHelpStr = "skimnotes remove: delete Skim notes from a 
PDF\nUsage: skimnotes remove PDF_FILE\n\nRemoves the Skim notes from the 
extended attributes of PDF_FILE.";
+static char *convertHelpStr = "skimnotes convert: convert between a PDF file 
and a PDF bundle\nUsage: skimnotes convert PDF_FILE|PDF_BUNDLE 
[PDF_BUNDLE|PDF_FILE]\n\nConverts a PDF file to a PDF bundle or a PDF bundle to 
a PDF file.\nUses a file with same base name as the first file argument if the 
second file argument is not provided.";
 static char *agentHelpStr = "skimnotes agent: run the Skim Notes agent\nUsage: 
skimnotes agent [SERVER_NAME]\n\nRuns a Skim Notes agent server with server 
name SERVER_NAME, to which a Cocoa application can connect using DO.\nWhen 
SERVER_NAME is not provided, a unique name is generated and returned on 
standard output.\nThe DO server conforms to the following formal [EMAIL 
PROTECTED] SKNAgentListenerProtocol\n- (bycopy NSData *)SkimNotesAtPath:(in 
bycopy NSString *)aFile;\n- (bycopy NSData *)RTFNotesAtPath:(in bycopy NSString 
*)aFile;\n- (bycopy NSData *)textNotesAtPath:(in bycopy NSString *)aFile 
encoding:(NSStringEncoding)encoding;[EMAIL PROTECTED]";
 static char *protocolHelpStr = "skimnotes protocol: write the DO server 
protocol to standard output\nUsage: skimnotes protocol\n\nWrite the DO server 
protocol for the agent to standard output.";
 static char *helpHelpStr = "skimnotes help: get help on the skimnotes 
tool\nUsage: skimnotes help [VERB]\n\nGet help on the verb VERB.";
@@ -22,6 +19,7 @@
 #define ACTION_GET_STRING       @"get"
 #define ACTION_SET_STRING       @"set"
 #define ACTION_REMOVE_STRING    @"remove"
+#define ACTION_CONVERT_STRING   @"convert"
 #define ACTION_AGENT_STRING     @"agent"
 #define ACTION_PROTOCOL_STRING  @"protocol"
 #define ACTION_VERSION_STRING   @"version"
@@ -35,6 +33,7 @@
 #define FORMAT_RTF_STRING   @"rtf"
 
 #define PDF_EXTENSION   @"pdf"
+#define PDFD_EXTENSION  @"pdfd"
 #define SKIM_EXTENSION  @"skim"
 #define TXT_EXTENSION   @"txt"
 #define TEXT_EXTENSION  @"text"
@@ -51,6 +50,7 @@
     SKNActionGet,
     SKNActionSet,
     SKNActionRemove,
+    SKNActionConvert,
     SKNActionAgent,
     SKNActionProtocol,
     SKNActionVersion,
@@ -71,6 +71,8 @@
         return SKNActionSet;
     else if ([actionString caseInsensitiveCompare:ACTION_REMOVE_STRING] == 
NSOrderedSame)
         return SKNActionRemove;
+    else if ([actionString caseInsensitiveCompare:ACTION_CONVERT_STRING] == 
NSOrderedSame)
+        return SKNActionConvert;
     else if ([actionString caseInsensitiveCompare:ACTION_AGENT_STRING] == 
NSOrderedSame)
         return SKNActionAgent;
     else if ([actionString caseInsensitiveCompare:ACTION_PROTOCOL_STRING] == 
NSOrderedSame)
@@ -149,6 +151,9 @@
             case SKNActionRemove:
                 WRITE_OUT(removeHelpStr);
                 break;
+            case SKNActionConvert:
+                WRITE_OUT(convertHelpStr);
+                break;
             case SKNActionAgent:
                 WRITE_OUT(agentHelpStr);
                 break;
@@ -199,17 +204,24 @@
         NSFileManager *fm = [NSFileManager defaultManager];
         NSString *pdfPath = SKNNormalizedPath([args objectAtIndex:offset]);
         NSString *notesPath = argc < offset + 2 ? nil : 
SKNNormalizedPath([args objectAtIndex:offset + 1]);
+        BOOL isBundle = NO;
         BOOL isDir = NO;
         NSError *error = nil;
         
+        if ([[pdfPath pathExtension] caseInsensitiveCompare:PDFD_EXTENSION] == 
NSOrderedSame)
+            isBundle = YES;
+        else if ([[pdfPath pathExtension] 
caseInsensitiveCompare:PDF_EXTENSION] != NSOrderedSame)
+            pdfPath = [[pdfPath stringByDeletingPathExtension] 
stringByAppendingPathExtension:PDF_EXTENSION];
+        
         if (action != SKNActionRemove && notesPath == nil) {
-            notesPath = [[pdfPath stringByDeletingPathExtension] 
stringByAppendingPathExtension:format == SKNFormatText ? TXT_EXTENSION : format 
== SKNFormatRTF ? RTF_EXTENSION : SKIM_EXTENSION];
+            notesPath = [pdfPath stringByDeletingPathExtension];
+            if (action == SKNActionConvert)
+                notesPath = [notesPath stringByAppendingPathExtension:isBundle 
? PDF_EXTENSION : PDFD_EXTENSION];
+            else
+                notesPath = [notesPath stringByAppendingPathExtension:format 
== SKNFormatText ? TXT_EXTENSION : format == SKNFormatRTF ? RTF_EXTENSION : 
SKIM_EXTENSION];
         }
         
-        if ([[pdfPath pathExtension] caseInsensitiveCompare:PDF_EXTENSION] != 
NSOrderedSame)
-            pdfPath = [[pdfPath stringByDeletingPathExtension] 
stringByAppendingPathExtension:PDF_EXTENSION];
-        
-        if ([fm fileExistsAtPath:pdfPath isDirectory:&isDir] == NO || isDir) {
+        if ([fm fileExistsAtPath:pdfPath isDirectory:&isDir] == NO || isBundle 
!= isDir) {
             error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOENT 
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"PDF file does not exist", 
NSLocalizedDescriptionKey, nil]];
         } else if (action == SKNActionGet) {
             NSData *data = nil;
@@ -222,21 +234,12 @@
                 else
                     format = SKNFormatSkim;
             }
-            SKNExtendedAttributeManager *eam = [SKNExtendedAttributeManager 
sharedManager];
-            if (format == SKNFormatSkim) {
-                data = [eam extendedAttributeNamed:SKIM_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error];
-                if (data == nil && [error code] == ENOATTR)
-                    data = [NSData data];
-            } else if (format == SKNFormatText) {
-                NSString *string = [eam 
propertyListFromExtendedAttributeNamed:SKIM_TEXT_NOTES_KEY atPath:pdfPath 
traverseLink:YES error:&error];
-                data = [string dataUsingEncoding:NSUTF8StringEncoding];
-                if (string == nil && [error code] == ENOATTR)
-                    data = [NSData data];
-            } else if (format == SKNFormatRTF) {
-                data = [eam extendedAttributeNamed:SKIM_RTF_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error];
-                if (data == nil && [error code] == ENOATTR)
-                    data = [NSData data];
-            }
+            if (format == SKNFormatSkim)
+                data = [fm SkimNotesAtPath:pdfPath error:&error];
+            else if (format == SKNFormatText)
+                data = [[fm SkimTextNotesAtPath:pdfPath error:&error] 
dataUsingEncoding:NSUTF8StringEncoding];
+            else if (format == SKNFormatRTF)
+                data = [fm SkimRTFNotesAtPath:pdfPath error:&error];
             if (data) {
                 if ([notesPath isEqualToString:STD_IN_OUT_FILE]) {
                     if ([data length])
@@ -260,26 +263,34 @@
                 data = [[NSFileHandle fileHandleWithStandardInput] 
readDataToEndOfFile];
             else
                 data = [NSData dataWithContentsOfFile:notesPath];
-            if (data) {
-                SKNExtendedAttributeManager *eam = 
[SKNExtendedAttributeManager sharedManager];
-                success = [eam removeExtendedAttribute:SKIM_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error] || [error code] == ENOATTR;
-                [eam removeExtendedAttribute:SKIM_RTF_NOTES_KEY atPath:pdfPath 
traverseLink:YES error:NULL];
-                [eam removeExtendedAttribute:SKIM_TEXT_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:NULL];
-                if (success && [data length])
-                    success = [eam setExtendedAttributeNamed:SKIM_NOTES_KEY 
toValue:data atPath:pdfPath options:0 error:&error];
+            if ([data length])
+                success = [fm writeSkimNotes:data textNotes:nil RTFNotes:nil 
atPath:pdfPath error:&error];
+            else if (data)
+                success = [fm removeSkimNotesAtPath:pdfPath error:&error];
+        } else if (action == SKNActionRemove) {
+            success = [fm removeSkimNotesAtPath:pdfPath error:&error];
+        } else if (action == SKNActionConvert) {
+            if (isBundle) {
+                success = [fm createDirectoryAtPath:notesPath attributes:nil];
+            } else {
+                NSString *pdfFilePath = nil;
+                NSArray *files = [fm subpathsAtPath:pdfPath];
+                NSString *filename = [[[pdfPath lastPathComponent] 
stringByDeletingPathExtension] stringByAppendingPathExtension:PDF_EXTENSION];
+                if ([files containsObject:filename] == NO) {
+                    NSUInteger idx = [[files 
valueForKeyPath:@"pathExtension.lowercaseString"] indexOfObject:PDF_EXTENSION];
+                    filename = idx == NSNotFound ? nil : [files 
objectAtIndex:idx];
+                }
+                if (filename)
+                    pdfFilePath = [pdfPath 
stringByAppendingPathComponent:filename];
+                success = [fm copyPath:pdfFilePath toPath:notesPath 
handler:nil];
             }
-        } else if (action == SKNActionRemove) {
-            SKNExtendedAttributeManager *eam = [SKNExtendedAttributeManager 
sharedManager];
-            BOOL success1 = [eam removeExtendedAttribute:SKIM_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error];
-            if (success1 == NO && [error code] == ENOATTR)
-                success1 = YES;
-            BOOL success2 = [eam removeExtendedAttribute:SKIM_RTF_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error];
-            if (success2 == NO && [error code] == ENOATTR)
-                success2 = YES;
-            BOOL success3 = [eam removeExtendedAttribute:SKIM_TEXT_NOTES_KEY 
atPath:pdfPath traverseLink:YES error:&error];
-            if (success3 == NO && [error code] == ENOATTR)
-                success3 = YES;
-            success = success1 && success2 && success3;
+            if (success) {
+                NSData *notesData = [fm SkimNotesAtPath:pdfPath error:&error];
+                NSString *textNotes = [fm SkimTextNotesAtPath:pdfPath 
error:&error];
+                NSData *rtfNotesData = [fm SkimRTFNotesAtPath:pdfPath 
error:&error];
+                if (notesData)
+                    success = [fm writeSkimNotes:notesData textNotes:textNotes 
RTFNotes:rtfNotesData atPath:notesPath error:&error];
+            }
         }
         
         if (success == NO && error)


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 the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit

Reply via email to