Re: Referring to file by Alias ^or^ path
Re: Referring to file by Alias ^or^ path
- Subject: Re: Referring to file by Alias ^or^ path
- From: Jerry Krinock <email@hidden>
- Date: Wed, 3 Jun 2009 19:15:26 -0700
On 2009 Jun 03, at 17:26, Bill Monk wrote:
Given that your category depends on FSNewAliasFromPath, which is
10.5-only, both blocks of
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4....
can be dispensed with.
Hehe. Well, like I said, my code had been "cobbled together over time
from various sources". But I believe we need the "modern" block,
Size size = GetAliasSize(aliasHandle) ;
AliasPtr aliasPtr = *aliasHandle ;
data = [NSData dataWithBytes:aliasPtr
length:size] ;
Might just as well say:
data = [NSData dataWithBytes:*aliasHandle length:size] ;
Yup.
AliasRecord aliasHeader = *((AliasPtr)[self bytes]) ;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
AliasPtr ap = &aliasHeader ;
AliasHandle ah = &ap ;
That sort of machination used to be known as "creating a fake
handle" - as distinguished from a "real" Handle, a pointer to a
pointer to relocatable block allocated by the Memory Manager . Doing
that would cause various havoc and crashes. You can get away with it
now because NewHandle ends up as a malloc call, and memory blocks
are no longer relocatable. Still, it just looks wrong to my eye.
Worse, in recent memory, fake handles could still cause trouble in
OS X in certain circumstances. (When I tried to google it, I found
my own post from 2005: http://www.cocoabuilder.com/archive/message/cocoa/2005/8/16/144407)
But anyway, easier to sidestep it with GetAliasSizeFromPtr. A one-
liner replaces seven lines:
nBytesAliasRecord = GetAliasSizeFromPtr( (AliasPtr)[self bytes] );
Yes, looks good. But to get seven lines you need to count the
comments and unnecessary #ifdefs ;)
// The full path returned by FSRefMakePath will NOT have a
trailing slash UNLESS
// the path is the root, i.e. @"/". In that case it will.
Thus, in order to return
// a standard result to which "/Filename.ext" should be
appended, we remove that:
if ([path length] == 1)
path = @"" ;
How come?
Don't ask me. I believe I pasted in that comment (and the
accompanying section of code) from some Apple Sample Code.
If the caller is correctly using stringByAppendingPathComponent:,
and not stringByAppendingString", this shouldn't be a problem. The
correct path for the root is @"/" and I'd think needing to return
anything else here implies a problem elsewhere.
Well, you can have a convention to add trailing slashes or not add
trailing slashes. Personally, I think that trailing slashes are more
conventional, but apparently whoever specified FSRefMakePath() does not.
NSLog(@"*** Tests which should fail ***") ; ...
...
...
TestPath(@"/") ;
That test only fails because you force it to fail, by taking steps
to return @"" for the root.
Yes, if something is documented to fail, you should test that it
fails :))
AliasManager returns the correct path; I'd rather see it return
that, and let callers either use stringByAppendingPathComponent, or
let it be their problem if they have special reasons for not using
it. But maybe that's just me...
Actually, my present purpose is only regarding files, not
directories. So I just tried to document what others had done, even
if it's odd.
Thanks for the comments. This code keeps looking better, and shorter!
Jerry
Here's the modified .m. The .h should have documentation added to
note that it requires Mac OS 10.5.
#import "NSData+FileAlias.h"
@implementation NSData (FileAlias)
+ (NSData*)aliasRecordFromPath:(NSString*)path {
if ([path length] == 0) {
return nil ;
}
const char* pathC = [path fileSystemRepresentation] ;
OSErr osErr ;
AliasHandle aliasHandle = NULL ;
osErr = FSNewAliasFromPath (
NULL,
pathC,
0,
&aliasHandle,
NULL
) ;
NSData* data = nil ;
if (
(osErr == noErr)
// ... File exists and we have a full alias
||
((osErr == fnfErr) && (aliasHandle != NULL))
// ... File does not exist and we have a minimal alias
) {
Size size = GetAliasSize(aliasHandle) ;
data = [NSData dataWithBytes:*aliasHandle
length:size] ;
}
return data ;
}
- (NSString*)pathFromAliasRecord {
unsigned short nBytesAliasRecord ;
/*
In Aliases.h, note that the AliasRecord struct is opaque if
MAC_OS_X_MIN_VERSION_REQUIRED >= MAC_OS_X_VERSION_10_4. In other
words, if the "Mac OS X Deployment Target" setting for your
project is
10.4 or later, the AliasRecord struct is opaque.
That's because AliasRecords, as you've noticed, get written to
disk but
are also referenced in data, which means that they often have to
be
big-endian even on little-endian systems. Rather than enumerate
the
cases in which you'd want big- or little-endian AliasRecords, we
made
the data type opaque and added new APIs which deal in native-
endian
data. They're Get/SetAliasUserType and GetAliasSize, and there
are
also FromPtr versions of each if you have an AliasRecord *
instead of
an AliasHandle.
Eric Albert, Apple */
nBytesAliasRecord = GetAliasSizeFromPtr((AliasPtr)[self bytes]);
Handle handle = NULL;
FSRef resolvedFSRef;
NSString* path = nil ;
int err = 0 ;
// Move the now-decoded data into the Handle.
if (PtrToHand([self bytes], &handle, nBytesAliasRecord) != noErr) {
NSLog(@"Internal Error 589-5451. Can't allocate handle for
alias") ;
err = 1 ;
}
if (err == 0) {
// Successfully created the handle
// Now try and resolve the alias
Boolean changed ;
OSErr osErr = FSResolveAlias(NULL,
(AliasHandle)handle,
&resolvedFSRef,
&changed) ;
if (osErr == noErr) {
// Alias was resolved. Now get its path from resolvedFSRef
char pathC[4096] ;
OSStatus osStatus = FSRefMakePath(
&resolvedFSRef,
(UInt8*)pathC,
sizeof(pathC)
) ;
if (osStatus != noErr) {
NSLog(@"Internal Error 959-2697. OSStatus %i from
FSResolveAlias", osStatus) ;
err = 1 ;
}
else {
path = [NSString stringWithCString:pathC
encoding:NSUTF8StringEncoding] ;
// The full path returned by FSRefMakePath will NOT
have a trailing slash UNLESS
// the path is the root, i.e. @"/". In that case it
will. Thus, in order to return
// a standard result to which "/Filename.ext" should
be appended, we remove that:
if ([path length] == 1)
path = @"" ;
}
}
else if (osErr == fnfErr) {
// The alias could not be resolved because the file does
not exist.
// However, we can still extract the expected path from
the alias.
osErr = FSCopyAliasInfo (
(AliasHandle)handle,
NULL,
NULL,
(CFStringRef*)&(path),
NULL,
NULL
) ;
// There may be a bug in the above function. If the
alias is to
// that of a nonexistent directory in the root, for
example,
// /Yousers
// Then the path returned will begin with two slashes.
// To work around that,
if ([path hasPrefix:@"//"]) {
path = [path substringFromIndex:1] ;
}
}
}
if (handle)
DisposeHandle(handle);
return path ;
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden