Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include "NSURL+misc.h"
#include "common.h"
- (NSString *)stringValueRelativeToURL:(NSURL *)_base {
/*
Sample:
- base: http://localhost:20000/dbd.woa/so/localhost/
self: http://localhost:20000/dbd.woa/so/localhost/Databases/A
+ base: http://localhost:20000/dbd.woa/so/localhost/
=> Databases/A
Note: on Panther Foundation the -path misses the trailing slash!
if (debugURLProcessing) {
NSLog(@"%s: same namespace, but no direct relative (%@, base %@) => '%@'",
- __PRETTY_FUNCTION__, self, _base, relPath);
+ __PRETTY_FUNCTION__,
+ [self absoluteString], [_base absoluteString], relPath);
}
return relPath;
}
return @"/";
}
- /* this is the same like the absoltute path, only without a leading "/" .. */
- return [p substringFromIndex:1];
+ /* this is the same like the absolute path, only without a leading "/" .. */
+ return [p characterAtIndex:0] == '/' ? [p substringFromIndex:1] : p;
}
-- (NSString *)urlPathRelativeToPath:(NSString *)_base {
+static NSString *calcRelativePathOfChildURL(NSString *self, NSString *_base) {
/*
- This can be used for URLs in the same namespace. It should
- never return an absolute path (it only does in error conditions).
- */
-
- if (_base == nil || [_base length] == 0) {
- NSLog(@"%s: invalid base (nil or length 0), using absolute path '%@' ...",
- __PRETTY_FUNCTION__, self);
- return self;
- }
-
- if ([_base isEqualToString:@"/"])
- return [self urlPathRelativeToRoot];
- if ([_base isEqualToString:self])
- return [self urlPathRelativeToSelf];
-
- if (debugURLProcessing)
- NSLog(@"%s: %@ relative to %@ ...", __PRETTY_FUNCTION__, self, _base);
-
- if ([self hasPrefix:_base]) {
- /*
the whole base URI is prefix of our URI:
case a)
b: "/a/b/c"
b=s is already catched above and s is guaranteed to be
longer than b.
- */
- unsigned blen;
- NSString *result;
+ */
+ unsigned blen;
+ NSString *result;
- if (debugURLProcessing)
+ if (debugURLProcessing)
NSLog(@"%s: has base as prefix ...", __PRETTY_FUNCTION__);
- blen = [_base length];
+ blen = [_base length];
- if ([_base characterAtIndex:(blen - 1)] == '/') {
+ if ([_base characterAtIndex:(blen - 1)] == '/') {
/* last char of 'b' is '/' => case b) */
result = [self substringFromIndex:blen];
- }
- else {
+ }
+ else {
/*
last char of 'b' is not a slash (either case a) or case c)),
both are handled in the same way (search last / ...)
/* no we have case b) ... */
result = [self substringFromIndex:(r.location + 1)];
}
- }
- return result;
}
- else {
- NSString *prefix;
- unsigned plen;
+ return result;
+}
+
+- (NSString *)commonDirPathPrefixWithString:(NSString *)_other {
+ // TODO: the implementation can probably be optimized a _LOT_
+ /* eg "/home/images/" vs "/home/index.html" => "/home/", _not_ "/home/i" ! */
+ NSString *s;
+ unsigned len;
+ NSRange r;
+
+ if (_other == self)
+ return self;
+
+ s = [self commonPrefixWithString:_other options:0];
+ len = [s length];
+ if (len == 0)
+ return s;
+ if ([s characterAtIndex:(len - 1)] == '/')
+ return s;
+
+ r = [s rangeOfString:@"/" options:NSBackwardsSearch];
+ if (r.length == 0) /* hm, can't happen? */
+ return nil;
+
+ return [s substringToIndex:(r.location + r.length)];;
+}
+
+static
+NSString *calcRelativePathOfNonChildURL(NSString *self, NSString *_base) {
+ unsigned numSlashes;
+ NSString *result;
+ NSString *prefix;
+ NSString *suffix;
+ unsigned plen;
+
+ prefix = [self commonDirPathPrefixWithString:_base];
+ plen = [prefix length];
+ suffix = [self substringFromIndex:plen];
+ numSlashes = 0;
- prefix = [self commonPrefixWithString:_base options:0];
- plen = [prefix length];
+ if (debugURLProcessing) {
+ NSLog(@"%s: does not have base as prefix, common '%@'\n"
+ @" self='%@'\n"
+ @" base='%@'\n"
+ @" suffix='%@'",
+ __PRETTY_FUNCTION__, prefix, self, _base, suffix);
+ }
- if (plen == 0) {
+ if (plen == 0) {
NSLog(@"%s: invalid strings, no common prefix ...: '%@' and '%@' !",
__PRETTY_FUNCTION__, self, _base);
return self;
- }
+ }
- if (plen == 1) {
+ if (plen == 1) {
/*
common prefix is root. That is, nothing in common:
b: "/a/b"
b: "/a/b/"
s: "/l"
>: "../../l"
+ (number of slashes without root * "..", then the trailer?)
*/
+ unsigned i, len;
+
+ len = [_base length];
if ([prefix characterAtIndex:0] != '/') {
NSLog(@"%s: invalid strings, common prefix '%@' is not '/': "
__PRETTY_FUNCTION__, self, _base, prefix);
}
- /* TODO: to be completed ... */
- return self;
- }
+ for (i = 1 /* skip root */; i < len; i++) {
+ if ([_base characterAtIndex:i] == '/')
+ numSlashes++;
+ }
+ }
+ else {
+ /*
+ base: /dev/en/projects/bsd/index.html
+ self: /dev/en/macosx/
+ => ../../macosx/
+ */
+ NSString *basesuffix;
+ unsigned i, len;
+
+ basesuffix = [_base substringFromIndex:plen];
+ len = [basesuffix length];
+
+ for (i = 0; i < len; i++) {
+ if ([basesuffix characterAtIndex:i] == '/')
+ numSlashes++;
+ }
+ }
+
+ if (debugURLProcessing)
+ NSLog(@"%s: slashes: %d", __PRETTY_FUNCTION__, numSlashes);
- /* TODO: to be completed ... */
+ /* optimization for some depths */
+ switch (numSlashes) {
+ case 0: /* no slashes in base: b:/a, s:/images/a => images/a */
+ result = suffix;
+ break;
+ case 1: /* one slash in base: b:/a/, s:/images/a => ../images/a, etc */
+ result = [@"../" stringByAppendingString:suffix];
+ break;
+ case 2: result = [@"../../" stringByAppendingString:suffix]; break;
+ case 3: result = [@"../../../" stringByAppendingString:suffix]; break;
+ case 4: result = [@"../../../../" stringByAppendingString:suffix]; break;
+ case 5: result = [@"../../../../../" stringByAppendingString:suffix];break;
+ default: {
+ NSMutableString *ms;
+ unsigned i;
+
+ ms = [NSMutableString stringWithCapacity:(numSlashes * 3)];
+ for (i = 0; i < numSlashes; i++)
+ [ms appendString:@"../"];
+ [ms appendString:suffix];
+ result = ms;
+ break;
+ }
}
+ if (debugURLProcessing)
+ NSLog(@"%s: => '%@'", __PRETTY_FUNCTION__, result);
+ return result;
+}
+
+- (NSString *)urlPathRelativeToPath:(NSString *)_base {
+ /*
+ This can be used for URLs in the same namespace. It should
+ never return an absolute path (it only does in error conditions).
+ */
+ // TODO: the implementation can probably be optimized a _LOT_
+
+ if (_base == nil || [_base length] == 0) {
+ NSLog(@"%s: invalid base (nil or length 0), using absolute path '%@' ...",
+ __PRETTY_FUNCTION__, self);
+ return self;
+ }
+
+ if ([_base isEqualToString:@"/"])
+ return [self urlPathRelativeToRoot];
+ if ([_base isEqualToString:self])
+ return [self urlPathRelativeToSelf];
+
+ if (debugURLProcessing)
+ NSLog(@"%s: %@ relative to %@ ...", __PRETTY_FUNCTION__, self, _base);
+
+ if ([self hasPrefix:_base])
+ return calcRelativePathOfChildURL(self, _base);
- return self;
+ return calcRelativePathOfNonChildURL(self, _base);
}
@end /* NSString(URLPathProcessing) */