2 * Copyright (c) 2003, 2004, 2006-2008, 2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 23, 2003 Allan Nathanson <ajn@apple.com>
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
49 CFMutableArrayRef pInfo
;
51 } addContext
, *addContextRef
;
54 static __inline__
void
55 my_CFDictionaryApplyFunction(CFDictionaryRef theDict
,
56 CFDictionaryApplierFunction applier
,
59 CFAllocatorRef myAllocator
;
60 CFDictionaryRef myDict
;
62 myAllocator
= CFGetAllocator(theDict
);
63 myDict
= CFDictionaryCreateCopy(myAllocator
, theDict
);
64 CFDictionaryApplyFunction(myDict
, applier
, context
);
71 identifyKeyForPattern(const void *key
, void *val
, void *context
)
73 CFStringRef storeKey
= (CFStringRef
)key
;
74 CFDictionaryRef storeValue
= (CFDictionaryRef
)val
;
75 CFMutableArrayRef pInfo
= ((addContextRef
)context
)->pInfo
;
76 regex_t
* preg
= ((addContextRef
)context
)->preg
;
83 if (!CFDictionaryContainsKey(storeValue
, kSCDData
)) {
84 /* if no data (yet) */
88 /* convert store key to C string */
89 len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey
), kCFStringEncodingASCII
) + 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"));
97 /* compare store key to new notification keys regular expression pattern */
98 reError
= regexec(preg
, str
, 0, NULL
, 0);
101 /* we've got a match */
102 CFArrayAppendValue(pInfo
, storeKey
);
110 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
111 SCLog(TRUE
, LOG_DEBUG
, CFSTR("identifyKeyForPattern regexec(): %s"), reErrBuf
);
118 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
124 patternCompile(CFStringRef pattern
, regex_t
*preg
, CFStringRef
*error
)
126 Boolean append
= FALSE
;
127 Boolean insert
= FALSE
;
134 if (CFStringGetLength(pattern
) == 0) {
135 SCLog(TRUE
, LOG_ERR
, CFSTR("patternCompile(): empty string"));
138 if (!CFStringHasPrefix(pattern
, CFSTR("^"))) {
142 if (!CFStringHasSuffix(pattern
, CFSTR("$")) ||
143 CFStringHasSuffix(pattern
, CFSTR("\\$"))) {
147 /* if regex pattern is not bounded at both ends */
148 if (insert
|| append
) {
149 pattern
= CFStringCreateWithFormat(NULL
,
157 len_c
= CFStringGetBytes(pattern
,
158 CFRangeMake(0, CFStringGetLength(pattern
)),
159 kCFStringEncodingASCII
,
166 SCLog(TRUE
, LOG_ERR
, CFSTR("patternCompile(): could not get buffer length for \"%@\""), pattern
);
167 len
= sizeof(str_q
) - 1;
169 if (++len
> (CFIndex
)sizeof(str_q
)) {
170 str
= CFAllocatorAllocate(NULL
, len
, 0);
172 ok
= (_SC_cfstring_to_cstring(pattern
, str
, len
, kCFStringEncodingASCII
) != NULL
);
173 if (insert
|| append
) {
179 reError
= regcomp(preg
, str
, REG_EXTENDED
);
183 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
184 *error
= CFStringCreateWithCString(NULL
, reErrBuf
, kCFStringEncodingASCII
);
186 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("patternCompile regcomp(%s) failed: %s"), str
, reErrBuf
);
191 *error
= CFRetain(CFSTR("could not convert pattern to regex string"));
193 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("%@"), *error
);
197 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
202 static CF_RETURNS_RETAINED CFMutableArrayRef
203 patternCopy(CFStringRef pattern
)
207 pInfo
= CFDictionaryGetValue(patternData
, pattern
);
208 return pInfo
? CFArrayCreateMutableCopy(NULL
, 0, pInfo
) : NULL
;
212 static CF_RETURNS_RETAINED CFMutableArrayRef
213 patternNew(CFStringRef pattern
)
216 CFStringRef err
= NULL
;
217 CFMutableArrayRef pInfo
;
218 CFMutableDataRef pRegex
;
219 CFArrayRef pSessions
;
221 /* create the pattern info */
222 pInfo
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
224 /* compile the regular expression from the pattern string. */
225 pRegex
= CFDataCreateMutable(NULL
, sizeof(regex_t
));
226 CFDataSetLength(pRegex
, sizeof(regex_t
));
227 /* ALIGN: CF aligns to >8 byte boundries */
228 if (!patternCompile(pattern
, (regex_t
*)(void *)CFDataGetBytePtr(pRegex
), &err
)) {
235 /* add the compiled regular expression to the pattern info */
236 CFArrayAppendValue(pInfo
, pRegex
);
238 /* add the initial (empty) list of sessions watching this pattern */
239 pSessions
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
240 CFArrayAppendValue(pInfo
, pSessions
);
241 CFRelease(pSessions
);
243 /* identify/add all existing keys that match the specified pattern */
244 context
.pInfo
= pInfo
;
245 /* ALIGN: CF aligns to >8 byte boundries */
246 context
.preg
= (regex_t
*)(void *)CFDataGetBytePtr(pRegex
);
247 my_CFDictionaryApplyFunction(storeData
,
248 (CFDictionaryApplierFunction
)identifyKeyForPattern
,
258 patternCopyMatches(CFStringRef pattern
)
260 Boolean isNew
= FALSE
;
262 CFMutableArrayRef pInfo
;
264 /* find (or create new instance of) this pattern */
265 pInfo
= patternCopy(pattern
);
268 pInfo
= patternNew(pattern
);
279 pRegex
= CFArrayGetValueAtIndex(pInfo
, 0);
280 /* ALIGN: CF aligns to >8 byte boundries */
281 regfree((regex_t
*)(void *)CFDataGetBytePtr(pRegex
));
284 CFArrayReplaceValues(pInfo
, CFRangeMake(0, 2), NULL
, 0);
285 keys
= CFArrayCreateCopy(NULL
, pInfo
);
294 patternAddSession(CFStringRef pattern
, CFNumberRef sessionNum
)
298 CFMutableArrayRef pInfo
;
299 CFMutableArrayRef pSessions
;
301 /* find (or create new instance of) this pattern */
302 pInfo
= patternCopy(pattern
);
305 pInfo
= patternNew(pattern
);
311 /* add this session as a pattern watcher */
312 pSessions
= (CFMutableArrayRef
)CFArrayGetValueAtIndex(pInfo
, 1);
313 pSessions
= CFArrayCreateMutableCopy(NULL
, 0, pSessions
);
314 CFArrayAppendValue(pSessions
, sessionNum
);
315 CFArraySetValueAtIndex(pInfo
, 1, pSessions
);
316 CFRelease(pSessions
);
318 /* update pattern watcher info */
319 CFDictionarySetValue(patternData
, pattern
, pInfo
);
321 /* add this session as a watcher of any existing keys */
322 n
= CFArrayGetCount(pInfo
);
323 for (i
= 2; i
< n
; i
++) {
324 CFStringRef matchingKey
;
326 matchingKey
= CFArrayGetValueAtIndex(pInfo
, i
);
327 _addWatcher(sessionNum
, matchingKey
);
337 patternRemoveSession(CFStringRef pattern
, CFNumberRef sessionNum
)
341 CFMutableArrayRef pInfo
;
343 CFMutableArrayRef pSessions
;
345 /* find instance of this pattern */
346 pInfo
= patternCopy(pattern
);
348 /* remove this session as a watcher from all matching keys */
349 n
= CFArrayGetCount(pInfo
);
350 for (i
= 2; i
< n
; i
++) {
351 CFStringRef matchingKey
;
353 matchingKey
= CFArrayGetValueAtIndex(pInfo
, i
);
354 _removeWatcher(sessionNum
, matchingKey
);
357 /* remove session from watchers */
358 pSessions
= (CFMutableArrayRef
)CFArrayGetValueAtIndex(pInfo
, 1);
359 n
= CFArrayGetCount(pSessions
);
361 /* if other sessions are watching this pattern */
363 pSessions
= CFArrayCreateMutableCopy(NULL
, 0, pSessions
);
364 i
= CFArrayGetFirstIndexOfValue(pSessions
, CFRangeMake(0, n
), sessionNum
);
365 CFArrayRemoveValueAtIndex(pSessions
, i
);
366 CFArraySetValueAtIndex(pInfo
, 1, pSessions
);
367 CFRelease(pSessions
);
369 CFDictionarySetValue(patternData
, pattern
, pInfo
);
371 /* if no other sessions are watching this pattern */
373 pRegex
= CFArrayGetValueAtIndex(pInfo
, 0);
374 /* ALIGN: CF aligns to >8 byte boundries */
375 regfree((regex_t
*)(void *)CFDataGetBytePtr(pRegex
));
376 CFDictionaryRemoveValue(patternData
, pattern
);
385 addKeyForPattern(const void *key
, void *val
, void *context
)
387 CFStringRef pattern
= (CFStringRef
)key
;
388 CFArrayRef pInfo
= (CFArrayRef
)val
;
389 CFStringRef storeKey
= (CFStringRef
)context
;
397 /* convert store key to C string */
398 len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey
), kCFStringEncodingASCII
) + 1;
399 if (len
> (CFIndex
)sizeof(str_q
))
400 str
= CFAllocatorAllocate(NULL
, len
, 0);
401 if (_SC_cfstring_to_cstring(storeKey
, str
, len
, kCFStringEncodingASCII
) == NULL
) {
402 SCLog(TRUE
, LOG_DEBUG
, CFSTR("addKeyForPattern(): could not convert store key to C string"));
406 /* compare new store key to regular expression pattern */
407 /* ALIGN: CF aligns to >8 byte boundries */
408 preg
= (regex_t
*)(void *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo
, 0));
409 reError
= regexec(preg
, str
, 0, NULL
, 0);
417 CFMutableArrayRef pInfo_new
;
418 CFArrayRef pSessions
;
421 pSessions
= CFArrayGetValueAtIndex(pInfo
, 1);
422 n
= CFArrayGetCount(pSessions
);
423 for (i
= 0; i
< n
; i
++) {
424 CFNumberRef sessionNum
= CFArrayGetValueAtIndex(pSessions
, i
);
426 _addWatcher(sessionNum
, storeKey
);
429 /* add key, update pattern watcher info */
430 pInfo_new
= CFArrayCreateMutableCopy(NULL
, 0, pInfo
);
431 CFArrayAppendValue(pInfo_new
, storeKey
);
432 CFDictionarySetValue(patternData
, pattern
, pInfo_new
);
433 CFRelease(pInfo_new
);
442 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
443 SCLog(TRUE
, LOG_DEBUG
, CFSTR("addKeyForPattern regexec(): %s"), reErrBuf
);
450 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
457 patternAddKey(CFStringRef key
)
459 void *context
= (void *)key
;
461 my_CFDictionaryApplyFunction(patternData
,
462 (CFDictionaryApplierFunction
)addKeyForPattern
,
470 removeKeyFromPattern(const void *key
, void *val
, void *context
)
472 CFStringRef pattern
= (CFStringRef
)key
;
473 CFArrayRef pInfo
= (CFArrayRef
)val
;
474 CFStringRef storeKey
= (CFStringRef
)context
;
478 CFMutableArrayRef pInfo_new
;
479 CFArrayRef pSessions
;
481 n
= CFArrayGetCount(pInfo
);
483 /* if no keys match this pattern */
487 i
= CFArrayGetFirstIndexOfValue(pInfo
, CFRangeMake(2, n
-2), storeKey
);
488 if (i
== kCFNotFound
) {
489 /* if this key wasn't matched by this pattern */
493 /* remove key from pattern info */
494 pInfo_new
= CFArrayCreateMutableCopy(NULL
, 0, pInfo
);
495 CFArrayRemoveValueAtIndex(pInfo_new
, i
);
497 /* remove watchers */
498 pSessions
= CFArrayGetValueAtIndex(pInfo_new
, 1);
499 n
= CFArrayGetCount(pSessions
);
500 for (i
= 0; i
< n
; i
++) {
501 CFNumberRef sessionNum
= CFArrayGetValueAtIndex(pSessions
, i
);
503 _removeWatcher(sessionNum
, storeKey
);
506 CFDictionarySetValue(patternData
, pattern
, pInfo_new
);
507 CFRelease(pInfo_new
);
514 patternRemoveKey(CFStringRef key
)
516 void *context
= (void *)key
;
518 my_CFDictionaryApplyFunction(patternData
,
519 (CFDictionaryApplierFunction
)removeKeyFromPattern
,