]> err.no Git - sope/blobdiff - sope-core/NGExtensions/FdExt.subproj/NSURL+misc.m
fixed to URL processing
[sope] / sope-core / NGExtensions / FdExt.subproj / NSURL+misc.m
index fca96e8d898d6c1d7d3fd7eb5142de6fd13e8061..7870f2de19fad1f4e49ce5c73f3aaefbee176f3d 100644 (file)
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include "NSURL+misc.h"
 #include "common.h"
@@ -86,8 +85,8 @@ static BOOL debugURLProcessing = NO;
 - (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!
@@ -145,7 +144,8 @@ static BOOL debugURLProcessing = NO;
   
   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;
 }
@@ -217,32 +217,12 @@ static BOOL isEqual(id o1, id o2) {
     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"
@@ -259,19 +239,19 @@ static BOOL isEqual(id o1, id o2) {
         
       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 / ...)
@@ -288,23 +268,62 @@ static BOOL isEqual(id o1, id o2) {
         /* 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"
@@ -314,7 +333,11 @@ static BOOL isEqual(id o1, id o2) {
           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 '/': "
@@ -322,14 +345,86 @@ static BOOL isEqual(id o1, id o2) {
               __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) */