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