]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSURL+misc.m
Fixes to NGCalendarDateRange.m
[sope] / sope-core / NGExtensions / FdExt.subproj / NSURL+misc.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "NSURL+misc.h"
24 #include "common.h"
25
26 static BOOL debugURLProcessing = NO;
27
28 @implementation NSURL(misc)
29
30 - (NSString *)pathWithCorrectTrailingSlash {
31 #if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
32   /*
33     At least on OSX 10.3 the -path method missing the trailing slash, eg:
34       http://localhost:20000/dbd.woa/so/localhost/
35     gives:
36       /dbd.woa/so/localhost
37   */
38   NSString *p;
39   
40   if ((p = [self path]) == nil)
41     return nil;
42   
43   if ([p hasSuffix:@"/"])
44     return p;
45
46   if (![[self absoluteString] hasSuffix:@"/"])
47     return p;
48   
49   /* so we are running into the bug ... */
50   return [p stringByAppendingString:@"/"];
51 #else
52   return [self path];
53 #endif
54 }
55
56 - (NSString *)stringByAddingFragmentAndQueryToPath:(NSString *)_path {
57   NSString *lFrag, *lQuery;
58   
59   if ([self isFileURL])
60     return _path;
61   
62   lFrag   = [self fragment];
63   lQuery  = [self query];
64   
65   if ((lFrag != nil) || (lQuery != nil)) {
66     NSMutableString *ms;
67     
68     ms = [NSMutableString stringWithCapacity:([_path length] + 32)];
69     
70     [ms appendString:_path];
71     
72     if (lFrag) {
73       [ms appendString:@"#"];
74       [ms appendString:lFrag];
75     }
76     if (lQuery) {
77       [ms appendString:@"?"];
78       [ms appendString:lQuery];
79     }
80     return ms;
81   }
82   else
83     return _path;
84 }
85
86 - (NSString *)stringValueRelativeToURL:(NSURL *)_base {
87   /*
88     Sample:
89       base: http://localhost:20000/dbd.woa/so/localhost/
90       self: http://localhost:20000/dbd.woa/so/localhost/Databases/A
91          => Databases/A
92     
93     Note: on Panther Foundation the -path misses the trailing slash!
94   */
95   NSString *relPath;
96   
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);
103     }
104     return relPath;
105   }
106   
107   /* check whether we are already marked relative to _base .. */
108   if ([self baseURL] == _base) {
109     NSString *p;
110     
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:@"/"];
117     }
118 #endif
119     p = [self stringByAddingFragmentAndQueryToPath:p];
120     if (debugURLProcessing) {
121       NSLog(@"%s: url base and _base match => '%@'", 
122             __PRETTY_FUNCTION__, p);
123     }
124     return p;
125   }
126   
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);
134     }
135     return relPath;
136   }
137   
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);
143   }
144   relPath = [self stringByAddingFragmentAndQueryToPath:relPath];
145   
146   if (debugURLProcessing) {
147     NSLog(@"%s: same namespace, but no direct relative (%@, base %@) => '%@'", 
148           __PRETTY_FUNCTION__, self, _base, relPath);
149   }
150   return relPath;
151 }
152
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];
157 }
158
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;
165   
166   if (![[self scheme] isEqualToString:[_url scheme]])
167     return NO;
168   
169   if (!isEqual([self host], [_url host]))
170     return NO;
171   if (!isEqual([self port], [_url port]))
172     return NO;
173   if (!isEqual([self user], [_url user]))
174     return NO;
175   
176   return YES;
177 }
178
179 @end /* NSURL */
180
181 @implementation NSString(URLPathProcessing)
182
183 - (NSString *)urlPathRelativeToSelf {
184   /*
185     eg:                "/a/b/c.html"
186     should resolve to: "c.html"
187     
188     Directories are a bit more difficult, eg:
189       "/a/b/c/"
190     is resolved to
191       "../c/"
192   */
193   NSString *p;
194   NSString *lp;
195   
196   p  = self;
197   lp = [p lastPathComponent];
198   
199   p = ([p hasSuffix:@"/"])
200     ? [NSString stringWithFormat:@"../%@/", p]
201     : lp;
202   return p;
203 }
204
205 - (NSString *)urlPathRelativeToRoot {
206   NSString *p;
207   
208   p = self;
209   
210   if ([p isEqualToString:@"/"])
211     /* don't know better ... what is root-relative-to-root ? */
212     return @"/";
213   
214   if ([p length] == 0) {
215     NSLog(@"%s: invalid path (length 0), using /: %@",
216           __PRETTY_FUNCTION__, self);
217     return @"/";
218   }
219   
220   /* this is the same like the absoltute path, only without a leading "/" .. */
221   return [p substringFromIndex:1];
222 }
223
224 - (NSString *)urlPathRelativeToPath:(NSString *)_base {
225   /*
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).
228   */
229   
230   if (_base == nil || [_base length] == 0) {
231     NSLog(@"%s: invalid base (nil or length 0), using absolute path '%@' ...",
232           __PRETTY_FUNCTION__, self);
233     return self;
234   }
235   
236   if ([_base isEqualToString:@"/"])
237     return [self urlPathRelativeToRoot];
238   if ([_base isEqualToString:self])
239     return [self urlPathRelativeToSelf];
240   
241   if (debugURLProcessing)
242     NSLog(@"%s: %@ relative to %@ ...", __PRETTY_FUNCTION__, self, _base);
243   
244   if ([self hasPrefix:_base]) {
245     /*
246       the whole base URI is prefix of our URI:
247         case a)
248           b: "/a/b/c"
249           s: "/a/b/c/d"
250           >: "c/d"
251         case b)
252           b: "/a/b/c/"
253           s: "/a/b/c/d"
254           >: "d"
255         case c)
256           b: "/a/b/c"
257           s: "/a/b/ccc/d"
258           >: "ccc/d"
259         
260       b=s is already catched above and s is guaranteed to be
261       longer than b.
262     */
263     unsigned blen;
264     NSString *result;
265     
266     if (debugURLProcessing)
267       NSLog(@"%s:   has base as prefix ...", __PRETTY_FUNCTION__);
268     blen = [_base length];
269     
270     if ([_base characterAtIndex:(blen - 1)] == '/') {
271       /* last char of 'b' is '/' => case b) */
272       result = [self substringFromIndex:blen];
273     }
274     else {
275       /*
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 / ...)
278       */
279       NSRange  r;
280         
281       r = [_base rangeOfString:@"/" options:NSBackwardsSearch];
282       if (r.length == 0) {
283         NSLog(@"%s: invalid base, found no '/': '%@' !",
284               __PRETTY_FUNCTION__, _base);
285         result = self;
286       }
287       else {
288         /* no we have case b) ... */
289         result = [self substringFromIndex:(r.location + 1)];
290       }
291     }
292     return result;
293   }
294   else {
295     NSString *prefix;
296     unsigned plen;
297     
298     prefix = [self commonPrefixWithString:_base options:0];
299     plen = [prefix length];
300     
301     if (plen == 0) {
302       NSLog(@"%s: invalid strings, no common prefix ...: '%@' and '%@' !",
303               __PRETTY_FUNCTION__, self, _base);
304       return self;
305     }
306     
307     if (plen == 1) {
308       /*
309         common prefix is root. That is, nothing in common:
310           b: "/a/b"
311           s: "/l"
312           >: "../l"
313           
314           b: "/a/b/"
315           s: "/l"
316           >: "../../l"
317       */
318       
319       if ([prefix characterAtIndex:0] != '/') {
320         NSLog(@"%s: invalid strings, common prefix '%@' is not '/': "
321               @"'%@' and '%@' !",
322               __PRETTY_FUNCTION__, self, _base, prefix);
323       }
324       
325       /* TODO: to be completed ... */
326       return self;
327     }
328     
329     /* TODO: to be completed ... */
330   }
331   
332   return self;
333 }
334
335 @end /* NSString(URLPathProcessing) */