]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/pattern.c
configd-130.tar.gz
[apple/configd.git] / configd.tproj / pattern.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * April 23, 2003 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32 #include "configd.h"
33 #include "pattern.h"
34
35
36 /*
37 * Notes:
38 *
39 * - pattern matching information is maintained in a dictionary (patternData).
40 * - the dictionary "key" is the regular expression being matched
41 * - the dictionary "value" is a CFArray with the following contents
42 * [0] = CFData consisting of the pre-compiled regular expression
43 * [1] = CFArray[CFNumber] consisting of the sessions watching this pattern
44 * [2-n] = dynamic store keys which match this pattern
45 */
46
47
48 typedef struct {
49 CFMutableArrayRef pInfo;
50 regex_t *preg;
51 } addContext, *addContextRef;
52
53
54 static __inline__ void
55 my_CFDictionaryApplyFunction(CFDictionaryRef theDict,
56 CFDictionaryApplierFunction applier,
57 void *context)
58 {
59 CFAllocatorRef myAllocator;
60 CFDictionaryRef myDict;
61
62 myAllocator = CFGetAllocator(theDict);
63 myDict = CFDictionaryCreateCopy(myAllocator, theDict);
64 CFDictionaryApplyFunction(myDict, applier, context);
65 CFRelease(myDict);
66 return;
67 }
68
69
70 static void
71 identifyKeyForPattern(const void *key, void *val, void *context)
72 {
73 CFStringRef storeKey = (CFStringRef)key;
74 CFDictionaryRef storeValue = (CFDictionaryRef)val;
75 CFMutableArrayRef pInfo = ((addContextRef)context)->pInfo;
76 regex_t * preg = ((addContextRef)context)->preg;
77
78 CFIndex len;
79 int reError;
80 char str_q[256];
81 char * str = str_q;
82
83 if (!CFDictionaryContainsKey(storeValue, kSCDData)) {
84 /* if no data (yet) */
85 return;
86 }
87
88 /* convert store key to C string */
89 len = CFStringGetLength(storeKey) + 1;
90 if (len > (CFIndex)sizeof(str_q))
91 str = CFAllocatorAllocate(NULL, len, 0);
92 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
93 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern(): could not convert store key to C string"));
94 goto done;
95 }
96
97 /* compare store key to new notification keys regular expression pattern */
98 reError = regexec(preg, str, 0, NULL, 0);
99 switch (reError) {
100 case 0 :
101 /* we've got a match */
102 CFArrayAppendValue(pInfo, storeKey);
103 break;
104 case REG_NOMATCH :
105 /* no match */
106 break;
107 default : {
108 char reErrBuf[256];
109
110 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
111 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern regexec(): %s"), reErrBuf);
112 break;
113 }
114 }
115
116 done :
117
118 if (str != str_q) CFAllocatorDeallocate(NULL, str);
119 return;
120 }
121
122
123 __private_extern__ Boolean
124 patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error)
125 {
126 Boolean append = FALSE;
127 Boolean insert = FALSE;
128 CFIndex len = 0;
129 Boolean ok;
130 char str_q[256];
131 char * str = str_q;
132
133 if (!CFStringHasPrefix(pattern, CFSTR("^"))) {
134 insert = TRUE;
135 }
136
137 if (!CFStringHasSuffix(pattern, CFSTR("$")) ||
138 CFStringHasSuffix(pattern, CFSTR("\\$"))) {
139 append = TRUE;
140 }
141
142 /* if regex pattern is not bounded at both ends */
143 if (insert || append) {
144 pattern = CFStringCreateWithFormat(NULL,
145 NULL,
146 CFSTR("%s%@%s"),
147 insert ? "^" : "",
148 pattern,
149 append ? "$" : "");
150 }
151
152 (void)CFStringGetBytes(pattern,
153 CFRangeMake(0, CFStringGetLength(pattern)),
154 kCFStringEncodingASCII,
155 0,
156 FALSE,
157 NULL,
158 0,
159 &len);
160 if (++len > (CFIndex)sizeof(str_q)) {
161 str = CFAllocatorAllocate(NULL, len, 0);
162 }
163 ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL);
164 if (insert || append) {
165 CFRelease(pattern);
166 }
167 if (ok) {
168 int reError;
169
170 reError = regcomp(preg, str, REG_EXTENDED);
171 if (reError != 0) {
172 char reErrBuf[256];
173
174 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
175 *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII);
176 #ifdef DEBUG
177 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("patternCompile regcomp(%s) failed: %s"), str, reErrBuf);
178 #endif /* DEBUG */
179 ok = FALSE;
180 }
181 } else {
182 *error = CFRetain(CFSTR("could not convert pattern to regex string"));
183 #ifdef DEBUG
184 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error);
185 #endif /* DEBUG */
186 }
187
188 if (str != str_q) CFAllocatorDeallocate(NULL, str);
189 return ok;
190 }
191
192
193 __private_extern__
194 CFMutableArrayRef
195 patternCopy(CFStringRef pattern)
196 {
197 CFArrayRef pInfo;
198
199 pInfo = CFDictionaryGetValue(patternData, pattern);
200 return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL;
201 }
202
203
204 __private_extern__
205 CFMutableArrayRef
206 patternNew(CFStringRef pattern)
207 {
208 addContext context;
209 CFStringRef err;
210 CFMutableArrayRef pInfo;
211 CFMutableDataRef pRegex;
212 CFArrayRef pSessions;
213
214 /* create the pattern info */
215 pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
216
217 /* compile the regular expression from the pattern string. */
218 pRegex = CFDataCreateMutable(NULL, sizeof(regex_t));
219 CFDataSetLength(pRegex, sizeof(regex_t));
220 if (!patternCompile(pattern, (regex_t *)CFDataGetBytePtr(pRegex), &err)) {
221 CFRelease(err);
222 CFRelease(pRegex);
223 CFRelease(pInfo);
224 return NULL;
225 }
226
227 /* add the compiled regular expression to the pattern info */
228 CFArrayAppendValue(pInfo, pRegex);
229
230 /* add the initial (empty) list of sessions watching this pattern */
231 pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
232 CFArrayAppendValue(pInfo, pSessions);
233 CFRelease(pSessions);
234
235 /* identify/add all existing keys that match the specified pattern */
236 context.pInfo = pInfo;
237 context.preg = (regex_t *)CFDataGetBytePtr(pRegex);
238 my_CFDictionaryApplyFunction(storeData,
239 (CFDictionaryApplierFunction)identifyKeyForPattern,
240 &context);
241
242 CFRelease(pRegex);
243 return pInfo;
244 }
245
246
247 __private_extern__
248 Boolean
249 patternAddSession(CFStringRef pattern, CFNumberRef sessionNum)
250 {
251 CFIndex i;
252 CFIndex n;
253 CFMutableArrayRef pInfo;
254 CFMutableArrayRef pSessions;
255
256 /* find (or create new instance of) this pattern */
257 pInfo = patternCopy(pattern);
258 if (!pInfo) {
259 /* if new pattern */
260 pInfo = patternNew(pattern);
261 if (!pInfo) {
262 return FALSE;
263 }
264 }
265
266 /* add this session as a pattern watcher */
267 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
268 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
269 CFArrayAppendValue(pSessions, sessionNum);
270 CFArraySetValueAtIndex(pInfo, 1, pSessions);
271 CFRelease(pSessions);
272
273 /* update pattern watcher info */
274 CFDictionarySetValue(patternData, pattern, pInfo);
275
276 /* add this session as a watcher of any existing keys */
277 n = CFArrayGetCount(pInfo);
278 for (i = 2; i < n; i++) {
279 CFStringRef matchingKey;
280
281 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
282 _addWatcher(sessionNum, matchingKey);
283 }
284
285 CFRelease(pInfo);
286 return TRUE;
287 }
288
289
290 __private_extern__
291 void
292 patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum)
293 {
294 CFIndex i;
295 CFIndex n;
296 CFMutableArrayRef pInfo;
297 CFDataRef pRegex;
298 CFMutableArrayRef pSessions;
299
300 /* find instance of this pattern */
301 pInfo = patternCopy(pattern);
302
303 /* remove this session as a watcher from all matching keys */
304 n = CFArrayGetCount(pInfo);
305 for (i = 2; i < n; i++) {
306 CFStringRef matchingKey;
307
308 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
309 _removeWatcher(sessionNum, matchingKey);
310 }
311
312 /* remove session from watchers */
313 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
314 n = CFArrayGetCount(pSessions);
315 if (n > 1) {
316 /* if other sessions are watching this pattern */
317
318 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
319 i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum);
320 CFArrayRemoveValueAtIndex(pSessions, i);
321 CFArraySetValueAtIndex(pInfo, 1, pSessions);
322 CFRelease(pSessions);
323
324 CFDictionarySetValue(patternData, pattern, pInfo);
325 } else {
326 /* if no other sessions are watching this pattern */
327
328 pRegex = CFArrayGetValueAtIndex(pInfo, 0);
329 regfree((regex_t *)CFDataGetBytePtr(pRegex));
330 CFDictionaryRemoveValue(patternData, pattern);
331 }
332
333 CFRelease(pInfo);
334 return;
335 }
336
337
338 static void
339 addKeyForPattern(const void *key, void *val, void *context)
340 {
341 CFStringRef pattern = (CFStringRef)key;
342 CFArrayRef pInfo = (CFArrayRef)val;
343 CFStringRef storeKey = (CFStringRef)context;
344
345 CFIndex len;
346 regex_t *preg;
347 int reError;
348 char str_q[256];
349 char * str = str_q;
350
351 /* convert store key to C string */
352 len = CFStringGetLength(storeKey) + 1;
353 if (len > (CFIndex)sizeof(str_q))
354 str = CFAllocatorAllocate(NULL, len, 0);
355 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
356 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern(): could not convert store key to C string"));
357 goto done;
358 }
359
360 /* compare new store key to regular expression pattern */
361 preg = (regex_t *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0));
362 reError = regexec(preg, str, 0, NULL, 0);
363 switch (reError) {
364 case 0 : {
365 /*
366 * we've got a match
367 */
368 CFIndex i;
369 CFIndex n;
370 CFMutableArrayRef pInfo_new;
371 CFArrayRef pSessions;
372
373 /* add watchers */
374 pSessions = CFArrayGetValueAtIndex(pInfo, 1);
375 n = CFArrayGetCount(pSessions);
376 for (i = 0; i < n; i++) {
377 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
378
379 _addWatcher(sessionNum, storeKey);
380 }
381
382 /* add key, update pattern watcher info */
383 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
384 CFArrayAppendValue(pInfo_new, storeKey);
385 CFDictionarySetValue(patternData, pattern, pInfo_new);
386 CFRelease(pInfo_new);
387 break;
388 }
389 case REG_NOMATCH :
390 /* no match */
391 break;
392 default : {
393 char reErrBuf[256];
394
395 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
396 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern regexec(): %s"), reErrBuf);
397 break;
398 }
399 }
400
401 done :
402
403 if (str != str_q) CFAllocatorDeallocate(NULL, str);
404 return;
405 }
406
407
408 __private_extern__
409 void
410 patternAddKey(CFStringRef key)
411 {
412 my_CFDictionaryApplyFunction(patternData,
413 (CFDictionaryApplierFunction)addKeyForPattern,
414 (void *)key);
415
416 return;
417 }
418
419
420 static void
421 removeKeyFromPattern(const void *key, void *val, void *context)
422 {
423 CFStringRef pattern = (CFStringRef)key;
424 CFArrayRef pInfo = (CFArrayRef)val;
425 CFStringRef storeKey = (CFStringRef)context;
426
427 CFIndex i;
428 CFIndex n;
429 CFMutableArrayRef pInfo_new;
430 CFArrayRef pSessions;
431
432 n = CFArrayGetCount(pInfo);
433 if (n <= 2) {
434 /* if no keys match this pattern */
435 return;
436 }
437
438 i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey);
439 if (i == kCFNotFound) {
440 /* if this key wasn't matched by this pattern */
441 return;
442 }
443
444 /* remove key from pattern info */
445 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
446 CFArrayRemoveValueAtIndex(pInfo_new, i);
447
448 /* remove watchers */
449 pSessions = CFArrayGetValueAtIndex(pInfo_new, 1);
450 n = CFArrayGetCount(pSessions);
451 for (i = 0; i < n; i++) {
452 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
453
454 _removeWatcher(sessionNum, storeKey);
455 }
456
457 CFDictionarySetValue(patternData, pattern, pInfo_new);
458 CFRelease(pInfo_new);
459 return;
460 }
461
462
463 __private_extern__
464 void
465 patternRemoveKey(CFStringRef key)
466 {
467 my_CFDictionaryApplyFunction(patternData,
468 (CFDictionaryApplierFunction)removeKeyFromPattern,
469 (void *)key);
470
471 return;
472 }