]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSString+misc.m
generic escaping, minor refactoring
[sope] / sope-core / NGExtensions / FdExt.subproj / NSString+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 "NSString+misc.h"
24 #include "common.h"
25
26 @interface NSStringVariableBindingException : NSException
27 @end
28
29 @implementation NSStringVariableBindingException
30 @end
31
32 @implementation NSObject(StringBindings)
33
34 - (NSString *)valueForStringBinding:(NSString *)_key {
35   if (_key == nil) return nil;
36   return [[self valueForKey:_key] stringValue];
37 }
38
39 @end /* NSObject(StringBindings) */
40
41 @implementation NSDictionary(StringBindings)
42
43 - (NSString *)valueForStringBinding:(NSString *)_key {
44   if (_key == nil) return nil;
45   return [[self objectForKey:_key] stringValue];
46 }
47
48 @end /* NSDictionary(StringBindings) */
49
50 @implementation NSString(misc)
51
52 - (NSSet *)bindingVariables
53 {
54   unsigned        len, pos = 0;
55   const char      *buf     = NULL;
56   NSMutableSet    *result  = nil;
57
58   result = [NSMutableSet set];
59   len    = [self cStringLength];  
60   buf    = [self cString];
61   
62   while (pos < len) {
63     unsigned startPos;
64     
65     if (pos + 1 == len) { /* last entry */
66       if (buf[pos] == '$') { /* found $ without end-char */
67         [[[NSStringVariableBindingException alloc]
68            initWithFormat:@"did not find end of variable for string %@", self]
69            raise];
70       }
71       break;
72     }
73     if (buf[pos] != '$') {
74       pos++;
75       continue;
76     }
77     
78     if (buf[pos + 1] == '$') { /* found $$ --> ignore*/
79       pos += 2;
80       continue;
81     }
82
83     /* process binding */
84     
85     startPos = pos;
86     
87     pos += 2; /* buf[pos + 1] != '$' */
88     while (pos < len) {
89       if (buf[pos] != '$')
90         pos++;
91       else
92         break;
93     }
94     if (pos == len) { /* end of string was reached */
95       [[[NSStringVariableBindingException alloc]
96                                           initWithFormat:@"didn`t find end of "
97                                             @"variable for string %@", self]
98                                           raise];
99     }
100     else {
101       NSString *key   = nil;
102
103       key = [[NSString alloc]
104                        initWithCStringNoCopy:(char*)buf + startPos + 1
105                        length:pos - startPos - 1
106                        freeWhenDone:NO];
107       [result addObject:key];
108       [key release];
109     }
110     pos++;
111   }
112   return [[result copy] autorelease];
113 }
114
115 - (NSString *)stringByReplacingVariablesWithBindings:(id)_bindings
116   stringForUnknownBindings:(NSString *)_unknown
117 {
118   unsigned        len, pos = 0;
119   const char      *buf     = NULL;
120   NSMutableString *str     = nil;
121   
122   str = [self mutableCopy];
123   len = [str cStringLength];  
124   buf = [str cString];
125   
126   while (pos < len) {
127     if (pos + 1 == len) { /* last entry */
128       if (buf[pos] == '$') { /* found $ without end-char */
129         [[[NSStringVariableBindingException alloc]
130            initWithFormat:@"did not find end of variable for string %@", self]
131           raise];
132       }
133       break;
134     }
135     if (buf[pos] == '$') {
136       if (buf[pos + 1] == '$') { /* found $$ --> $ */
137         [str deleteCharactersInRange:NSMakeRange(pos, 1)];
138         buf = [str cString];
139         len = [str cStringLength];
140       }
141       else {
142         unsigned startPos = pos;
143
144         pos += 2; /* buf[pos + 1] != '$' */
145         while (pos < len) {
146           if (buf[pos] != '$')
147             pos++;
148           else
149             break;
150         }
151         if (pos == len) { /* end of string was reached */
152           [[[NSStringVariableBindingException alloc]
153              initWithFormat:@"did not find end of variable for string %@", 
154              self] raise];
155         }
156         else {
157           NSString *key;
158           NSString *value;
159
160           key = [[NSString alloc]
161                            initWithCStringNoCopy:(char*)buf + startPos + 1
162                            length:pos - startPos - 1
163                            freeWhenDone:NO];
164           
165           if ((value = [_bindings valueForStringBinding:key]) == nil) {
166             if (_unknown == nil) {
167               [[[NSStringVariableBindingException alloc]
168                           initWithFormat:@"did not find binding for "
169                                          @"name %@ in binding-dictionary %@",
170                                          [key autorelease], _bindings] raise];
171             }
172             else
173               value = _unknown;
174           }
175           [key release]; key = nil;
176           [str replaceCharactersInRange:
177                  NSMakeRange(startPos, pos - startPos + 1)
178                withString:value];
179           buf = [str cString];
180           len = [str cStringLength];
181           pos = startPos - 1 + [value length];
182         }
183       }
184     }
185     pos++;
186   }
187   {
188     id tmp = str;
189     str = [str copy];
190     [tmp release]; tmp = nil;
191   }
192   return [str autorelease];
193 }
194
195 - (NSString *)stringByReplacingVariablesWithBindings:(id)_bindings {
196   return [self stringByReplacingVariablesWithBindings:_bindings
197                stringForUnknownBindings:nil];
198 }
199
200 @end /* NSString(misc) */
201
202
203 @implementation NSString(FilePathVersioningMethods)
204
205 /*
206   "/path/file.txt;1"
207 */
208 - (NSString *)pathVersion {
209   NSRange r;
210
211   r = [self rangeOfString:@";"];
212   if (r.length > 0) {
213     return ([self length] > r.location)
214       ? [self substringFromIndex:(r.location + r.length)]
215       : @"";
216   }
217   return nil;
218 }
219
220 - (NSString *)stringByDeletingPathVersion {
221   NSRange r;
222
223   r = [self rangeOfString:@";"];
224   return (r.length > 0)
225     ? [self substringToIndex:r.location]
226     : self;
227 }
228
229 - (NSString *)stringByAppendingPathVersion:(NSString *)_version {
230   return [[self stringByAppendingString:@";"] 
231                 stringByAppendingString:_version];
232 }
233
234 @end /* NSString(FilePathMethodsVersioning) */
235
236 @implementation NSString(NGScanning)
237
238 - (NSRange)rangeOfString:(NSString *)_s 
239   skipQuotes:(NSString *)_quotes
240   escapedByChar:(unichar)_escape
241 {
242   // TODO: speed ...
243   // TODO: check correctness with invalid input !
244   static NSRange notFound = { 0, 0 };
245   NSCharacterSet *quotes;
246   unsigned i, len, slen;
247   unichar sc;
248   
249   if ((slen = [_s length]) == 0)
250     return notFound;
251   if ((len = [self length]) < slen) /* to short */
252     return notFound;
253   
254   if ([_quotes length] == 0)
255     _quotes = @"'\"";
256   quotes = [NSCharacterSet characterSetWithCharactersInString:_quotes];
257   
258   sc = [_s characterAtIndex:0];
259   
260   for (i = 0; i < len; i++) {
261     unichar c;
262     
263     c = [self characterAtIndex:i];
264     
265     if (c == sc) {
266       /* start search section */
267       if (slen == 1)
268         return NSMakeRange(i, 1);
269       
270       if ([[self substringFromIndex:i] hasPrefix:_s])
271         return NSMakeRange(i, slen);
272     }
273     else if ([quotes characterIsMember:c]) {
274       /* skip quotes */
275       for (i++; i < len && ![quotes characterIsMember:c]; i++) {
276         if (c == _escape) {
277           i++; /* skip next char (eg \') */
278           continue;
279         }
280       }
281     }
282   }
283   
284   return notFound;
285 }
286
287 - (NSRange)rangeOfString:(NSString *)_s skipQuotes:(NSString *)_quotes {
288   return [self rangeOfString:_s skipQuotes:_quotes escapedByChar:'\\'];
289 }
290
291 @end /* NSString(NGScanning) */
292
293 // linking
294
295 void __link_NSString_misc(void) {
296   __link_NSString_misc();
297 }