Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There's really a couple of different parts to your question. By my reading, you want:</p> <blockquote> <p><strong>1</strong> a way to tell if two different paths are the same on-disk file</p> <p><strong>2</strong> a canonical name for the file on disk, with the proper casing</p> </blockquote> <p>There's a third issue that gets mixed in, as well, having to do with <a href="http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/Articles/DisplayNames.html#//apple_ref/doc/uid/20002298" rel="noreferrer">Display Names</a>, because in OS X a file can localize its name and appear differently for different locales. So let's add</p> <blockquote> <p><strong>3</strong> a way to get the display name, because we might want to cache things depending on how the user sees the file system, not how the file system appears in the terminal.</p> </blockquote> <p>We can solve <strong>1</strong> with the FSRef trick pointed out by @boaz-stuller. Or here's some code that does it using higher-level Cocoa calls, which saves us a little bit of memory juggling (since we can let the <code>NSAutoreleasePool</code> do it for us):</p> <pre><code>long getInode(NSString* path) { NSFileManager* fm = [NSFileManager defaultManager]; NSError* error; NSDictionary* info = [fm attributesOfItemAtPath:path error:&amp;error]; NSNumber* inode = [info objectForKey:NSFileSystemFileNumber]; return [inode longValue]; } </code></pre> <p>But to solve <strong>2</strong>, we've got to use FSRefs to find out the canonical casing of the file:</p> <pre><code>NSString* getActualPath(NSString* path) { FSRef ref; OSStatus sts; UInt8* actualPath; //first get an FSRef for the path sts = FSPathMakeRef((const UInt8 *)[path UTF8String], &amp;ref, NULL); if (sts) return [NSString stringWithFormat:@"Error #%d making ref.", sts]; //then get a path from the FSRef actualPath = malloc(sizeof(UInt8)*MAX_PATH_LENGTH); sts = FSRefMakePath(&amp;ref, actualPath, MAX_PATH_LENGTH); if (sts) return [NSString stringWithFormat:@"Error #%d making path.", sts]; return [NSString stringWithUTF8String:(const char*)actualPath]; } </code></pre> <p>That's not bad at all, but we're still happy when we can solve <strong>3</strong> with Cocoa methods:</p> <pre><code>NSString* getDisplayPath(NSString* path) { NSFileManager* fm = [NSFileManager defaultManager]; NSString* mine = [fm displayNameAtPath:path]; NSString* parentPath = [path stringByDeletingLastPathComponent]; NSString* parents = [@"/" isEqualToString:parentPath] ? @"" : getDisplayPath(parentPath); return [NSString stringWithFormat:@"%@/%@", parents, mine]; } </code></pre> <p>Finally, we can add a bit of driver code and tie this all together into a CoreFoundation command line tool (I had to add the AppKit framework to get this to compile).</p> <pre><code>NSString* fileInfoString(NSString* path) { long inode = getInode(path); return [NSString stringWithFormat: @"\t%@ [inode #%d]\n\t\tis actually %@\n\t\tand displays as %@", path, inode, getActualPath(path), getDisplayPath(path)]; } int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; if (argc &lt; 2) { NSLog(@"Usage: %s &lt;path1&gt; [&lt;path2&gt;]", argv[0]); return -1; } NSString* path1 = [NSString stringWithCString:argv[1]]; NSString* path2 = argc &gt; 2 ? [NSString stringWithCString:argv[1]] : [path1 uppercaseString]; long inode1 = getInode(path1); long inode2 = getInode(path2); NSString* prefix = [NSString stringWithFormat: @"Comparing Files:\n%@\n%@", fileInfoString(path1), fileInfoString(path2)]; int retval = 0; if (inode1 == inode2) { NSLog(@"%@\nSame file.", prefix); } else { NSLog(@"%@\nDifferent files.", prefix); retval = 1; } [pool drain]; return retval; } </code></pre> <p>Now, we can put it all together and run it:</p> <pre> $ checkpath /users/tal 2008-12-15 23:59:10.605 checkpath[22375:10b] Comparing Files: /users/tal [inode #1061692] is actually /Users/tal and displays as /Users/tal /USERS/TAL [inode #1061692] is actually /Users/tal and displays as /Users/tal Same file. </pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload