2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "NSURL+misc.h"
26 static BOOL debugURLProcessing = NO;
28 @implementation NSURL(misc)
30 - (NSString *)pathWithCorrectTrailingSlash {
31 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
33 At least on OSX 10.3 the -path method missing the trailing slash, eg:
34 http://localhost:20000/dbd.woa/so/localhost/
40 if ((p = [self path]) == nil)
43 if ([p hasSuffix:@"/"])
46 if (![[self absoluteString] hasSuffix:@"/"])
49 /* so we are running into the bug ... */
50 return [p stringByAppendingString:@"/"];
56 - (NSString *)stringByAddingFragmentAndQueryToPath:(NSString *)_path {
57 NSString *lFrag, *lQuery;
62 lFrag = [self fragment];
63 lQuery = [self query];
65 if ((lFrag != nil) || (lQuery != nil)) {
68 ms = [NSMutableString stringWithCapacity:([_path length] + 32)];
70 [ms appendString:_path];
73 [ms appendString:@"#"];
74 [ms appendString:lFrag];
77 [ms appendString:@"?"];
78 [ms appendString:lQuery];
86 - (NSString *)stringValueRelativeToURL:(NSURL *)_base {
89 base: http://localhost:20000/dbd.woa/so/localhost/
90 self: http://localhost:20000/dbd.woa/so/localhost/Databases/A
93 Note: on Panther Foundation the -path misses the trailing slash!
97 if (_base == self || _base == nil) {
98 relPath = [[self pathWithCorrectTrailingSlash] urlPathRelativeToSelf];
99 relPath = [self stringByAddingFragmentAndQueryToPath:relPath];
100 if (debugURLProcessing) {
101 NSLog(@"%s: no base or base is self => '%@'",
102 __PRETTY_FUNCTION__, relPath);
107 /* check whether we are already marked relative to _base .. */
108 if ([self baseURL] == _base) {
111 p = [self relativePath];
112 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
113 /* see -pathWithCorrectTrailingSlash for bug description ... */
114 if (![p hasSuffix:@"/"]) {
115 if ([[self absoluteString] hasSuffix:@"/"])
116 p = [p stringByAppendingString:@"/"];
119 p = [self stringByAddingFragmentAndQueryToPath:p];
120 if (debugURLProcessing) {
121 NSLog(@"%s: url base and _base match => '%@'",
122 __PRETTY_FUNCTION__, p);
127 /* check whether we are in the same path namespace ... */
128 if (![self isInSameNamespaceWithURL:_base]) {
129 /* need to return full URL */
130 relPath = [self absoluteString];
131 if (debugURLProcessing) {
132 NSLog(@"%s: url is in different namespace from base => '%@'",
133 __PRETTY_FUNCTION__, relPath);
138 relPath = [[self pathWithCorrectTrailingSlash]
139 urlPathRelativeToPath:[_base pathWithCorrectTrailingSlash]];
140 if (debugURLProcessing) {
141 NSLog(@"%s: path '%@', base-path '%@' => rel '%@'", __PRETTY_FUNCTION__,
142 [self path], [_base path], relPath);
144 relPath = [self stringByAddingFragmentAndQueryToPath:relPath];
146 if (debugURLProcessing) {
147 NSLog(@"%s: same namespace, but no direct relative (%@, base %@) => '%@'",
148 __PRETTY_FUNCTION__, self, _base, relPath);
153 static BOOL isEqual(id o1, id o2) {
154 if (o1 == o2) return YES;
155 if (o1 == nil || o2 == nil) return NO;
156 return [o1 isEqual:o2];
159 - (BOOL)isInSameNamespaceWithURL:(NSURL *)_url {
160 if (_url == nil) return NO;
161 if (_url == self) return YES;
162 if ([self isFileURL] && [_url isFileURL]) return YES;
163 if ([self baseURL] == _url) return YES;
164 if ([_url baseURL] == self) return YES;
166 if (![[self scheme] isEqualToString:[_url scheme]])
169 if (!isEqual([self host], [_url host]))
171 if (!isEqual([self port], [_url port]))
173 if (!isEqual([self user], [_url user]))
181 @implementation NSString(URLPathProcessing)
183 - (NSString *)urlPathRelativeToSelf {
186 should resolve to: "c.html"
188 Directories are a bit more difficult, eg:
197 lp = [p lastPathComponent];
199 p = ([p hasSuffix:@"/"])
200 ? [NSString stringWithFormat:@"../%@/", p]
205 - (NSString *)urlPathRelativeToRoot {
210 if ([p isEqualToString:@"/"])
211 /* don't know better ... what is root-relative-to-root ? */
214 if ([p length] == 0) {
215 NSLog(@"%s: invalid path (length 0), using /: %@",
216 __PRETTY_FUNCTION__, self);
220 /* this is the same like the absoltute path, only without a leading "/" .. */
221 return [p substringFromIndex:1];
224 - (NSString *)urlPathRelativeToPath:(NSString *)_base {
226 This can be used for URLs in the same namespace. It should
227 never return an absolute path (it only does in error conditions).
230 if (_base == nil || [_base length] == 0) {
231 NSLog(@"%s: invalid base (nil or length 0), using absolute path '%@' ...",
232 __PRETTY_FUNCTION__, self);
236 if ([_base isEqualToString:@"/"])
237 return [self urlPathRelativeToRoot];
238 if ([_base isEqualToString:self])
239 return [self urlPathRelativeToSelf];
241 if (debugURLProcessing)
242 NSLog(@"%s: %@ relative to %@ ...", __PRETTY_FUNCTION__, self, _base);
244 if ([self hasPrefix:_base]) {
246 the whole base URI is prefix of our URI:
260 b=s is already catched above and s is guaranteed to be
266 if (debugURLProcessing)
267 NSLog(@"%s: has base as prefix ...", __PRETTY_FUNCTION__);
268 blen = [_base length];
270 if ([_base characterAtIndex:(blen - 1)] == '/') {
271 /* last char of 'b' is '/' => case b) */
272 result = [self substringFromIndex:blen];
276 last char of 'b' is not a slash (either case a) or case c)),
277 both are handled in the same way (search last / ...)
281 r = [_base rangeOfString:@"/" options:NSBackwardsSearch];
283 NSLog(@"%s: invalid base, found no '/': '%@' !",
284 __PRETTY_FUNCTION__, _base);
288 /* no we have case b) ... */
289 result = [self substringFromIndex:(r.location + 1)];
298 prefix = [self commonPrefixWithString:_base options:0];
299 plen = [prefix length];
302 NSLog(@"%s: invalid strings, no common prefix ...: '%@' and '%@' !",
303 __PRETTY_FUNCTION__, self, _base);
309 common prefix is root. That is, nothing in common:
319 if ([prefix characterAtIndex:0] != '/') {
320 NSLog(@"%s: invalid strings, common prefix '%@' is not '/': "
322 __PRETTY_FUNCTION__, self, _base, prefix);
325 /* TODO: to be completed ... */
329 /* TODO: to be completed ... */
335 @end /* NSString(URLPathProcessing) */