2 * Copyright (c) 2003, 2004, 2006-2008, 2011, 2012, 2015 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 keyMatchesPattern(CFStringRef key
, CFDataRef pRegex
)
74 Boolean match
= FALSE
;
80 /* convert store key to C string */
81 len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(key
), kCFStringEncodingASCII
) + 1;
82 if (len
> (CFIndex
)sizeof(str_q
))
83 str
= CFAllocatorAllocate(NULL
, len
, 0);
84 if (_SC_cfstring_to_cstring(key
, str
, len
, kCFStringEncodingASCII
) == NULL
) {
85 SC_log(LOG_INFO
, "could not convert store key to C string");
89 /* ALIGN: CF aligns to >8 byte boundries */
90 preg
= (regex_t
*)(void *)CFDataGetBytePtr(pRegex
);
92 /* compare key to regular expression pattern */
93 reError
= regexec(preg
, str
, 0, NULL
, 0);
104 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
105 SC_log(LOG_INFO
, "regexec() failed: %s", reErrBuf
);
112 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
118 identifyKeyForPattern(const void *key
, void *val
, void *context
)
120 CFStringRef storeKey
= (CFStringRef
)key
;
121 CFDictionaryRef storeValue
= (CFDictionaryRef
)val
;
122 CFMutableArrayRef pInfo
= ((addContextRef
)context
)->pInfo
;
123 CFDataRef pRegex
= ((addContextRef
)context
)->pRegex
;
125 if (!CFDictionaryContainsKey(storeValue
, kSCDData
)) {
126 /* if no data (yet) */
130 if (keyMatchesPattern(storeKey
, pRegex
)) {
131 /* if we've got a match */
132 CFArrayAppendValue(pInfo
, storeKey
);
140 patternCompile(CFStringRef pattern
, CFDataRef pRegex
, CFStringRef
*error
)
142 Boolean append
= FALSE
;
143 Boolean insert
= FALSE
;
150 if (CFStringGetLength(pattern
) == 0) {
151 SC_log(LOG_NOTICE
, "empty regex pattern");
154 if (!CFStringHasPrefix(pattern
, CFSTR("^"))) {
158 if (!CFStringHasSuffix(pattern
, CFSTR("$")) ||
159 CFStringHasSuffix(pattern
, CFSTR("\\$"))) {
163 /* if regex pattern is not bounded at both ends */
164 if (insert
|| append
) {
165 pattern
= CFStringCreateWithFormat(NULL
,
173 len_c
= CFStringGetBytes(pattern
,
174 CFRangeMake(0, CFStringGetLength(pattern
)),
175 kCFStringEncodingASCII
,
182 SC_log(LOG_NOTICE
, "could not get buffer length for \"%@\"", pattern
);
183 len
= sizeof(str_q
) - 1;
185 if (++len
> (CFIndex
)sizeof(str_q
)) {
186 str
= CFAllocatorAllocate(NULL
, len
, 0);
188 ok
= (_SC_cfstring_to_cstring(pattern
, str
, len
, kCFStringEncodingASCII
) != NULL
);
189 if (insert
|| append
) {
196 /* ALIGN: CF aligns to >8 byte boundries */
197 preg
= (regex_t
*)(void *)CFDataGetBytePtr(pRegex
);
199 reError
= regcomp(preg
, str
, REG_EXTENDED
);
203 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
204 *error
= CFStringCreateWithCString(NULL
, reErrBuf
, kCFStringEncodingASCII
);
206 SC_log(LOG_DEBUG
, "regcomp(%s) failed: %s", str
, reErrBuf
);
211 *error
= CFRetain(CFSTR("could not convert pattern to regex string"));
213 SC_log(LOG_DEBUG
, "%@", *error
);
217 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
223 patternRelease(CFDataRef pRegex
)
227 /* ALIGN: CF aligns to >8 byte boundries */
228 preg
= (regex_t
*)(void *)CFDataGetBytePtr(pRegex
);
235 static CF_RETURNS_RETAINED CFMutableArrayRef
236 patternCopy(CFStringRef pattern
)
240 pInfo
= CFDictionaryGetValue(patternData
, pattern
);
241 return (pInfo
!= NULL
) ? CFArrayCreateMutableCopy(NULL
, 0, pInfo
) : NULL
;
245 static CF_RETURNS_RETAINED CFMutableArrayRef
246 patternNew(CFStringRef pattern
)
249 CFStringRef err
= NULL
;
250 CFMutableArrayRef pInfo
;
251 CFMutableDataRef pRegex
;
252 CFArrayRef pSessions
;
254 /* create the pattern info */
255 pInfo
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
257 /* compile the regular expression from the pattern string. */
258 pRegex
= CFDataCreateMutable(NULL
, sizeof(regex_t
));
259 CFDataSetLength(pRegex
, sizeof(regex_t
));
260 if (!patternCompile(pattern
, pRegex
, &err
)) {
267 /* add the compiled regular expression to the pattern info */
268 CFArrayAppendValue(pInfo
, pRegex
);
270 /* add the initial (empty) list of sessions watching this pattern */
271 pSessions
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
272 CFArrayAppendValue(pInfo
, pSessions
);
273 CFRelease(pSessions
);
275 /* identify/add all existing keys that match the specified pattern */
276 context
.pInfo
= pInfo
;
277 context
.pRegex
= pRegex
;
278 my_CFDictionaryApplyFunction(storeData
,
279 (CFDictionaryApplierFunction
)identifyKeyForPattern
,
289 patternCopyMatches(CFStringRef pattern
)
291 Boolean isNew
= FALSE
;
293 CFMutableArrayRef pInfo
;
295 /* find (or create new instance of) this pattern */
296 pInfo
= patternCopy(pattern
);
299 pInfo
= patternNew(pattern
);
310 pRegex
= CFArrayGetValueAtIndex(pInfo
, 0);
311 patternRelease(pRegex
);
314 CFArrayReplaceValues(pInfo
, CFRangeMake(0, 2), NULL
, 0);
315 keys
= CFArrayCreateCopy(NULL
, pInfo
);
324 patternKeyMatches(CFStringRef pattern
, CFStringRef key
)
326 Boolean isNew
= FALSE
;
327 Boolean match
= FALSE
;
328 CFMutableArrayRef pInfo
;
331 /* find (or create new instance of) this pattern */
332 pInfo
= patternCopy(pattern
);
336 /* if existing pattern, check if known key */
337 n
= CFArrayGetCount(pInfo
);
339 CFArrayContainsValue(pInfo
, CFRangeMake(2, n
- 2), key
);
345 pInfo
= patternNew(pattern
);
353 pRegex
= CFArrayGetValueAtIndex(pInfo
, 0);
354 match
= keyMatchesPattern(key
, pRegex
);
357 patternRelease(pRegex
);
370 patternAddSession(CFStringRef pattern
, CFNumberRef sessionNum
)
374 CFMutableArrayRef pInfo
;
375 CFMutableArrayRef pSessions
;
377 /* find (or create new instance of) this pattern */
378 pInfo
= patternCopy(pattern
);
381 pInfo
= patternNew(pattern
);
387 /* add this session as a pattern watcher */
388 pSessions
= (CFMutableArrayRef
)CFArrayGetValueAtIndex(pInfo
, 1);
389 pSessions
= CFArrayCreateMutableCopy(NULL
, 0, pSessions
);
390 CFArrayAppendValue(pSessions
, sessionNum
);
391 CFArraySetValueAtIndex(pInfo
, 1, pSessions
);
392 CFRelease(pSessions
);
394 /* update pattern watcher info */
395 CFDictionarySetValue(patternData
, pattern
, pInfo
);
397 /* add this session as a watcher of any existing keys */
398 n
= CFArrayGetCount(pInfo
);
399 for (i
= 2; i
< n
; i
++) {
400 CFStringRef matchingKey
;
402 matchingKey
= CFArrayGetValueAtIndex(pInfo
, i
);
403 _addWatcher(sessionNum
, matchingKey
);
413 patternRemoveSession(CFStringRef pattern
, CFNumberRef sessionNum
)
417 CFMutableArrayRef pInfo
;
419 CFMutableArrayRef pSessions
;
421 /* find instance of this pattern */
422 pInfo
= patternCopy(pattern
);
423 assert(pInfo
!= NULL
);
425 /* remove this session as a watcher from all matching keys */
426 n
= CFArrayGetCount(pInfo
);
427 for (i
= 2; i
< n
; i
++) {
428 CFStringRef matchingKey
;
430 matchingKey
= CFArrayGetValueAtIndex(pInfo
, i
);
431 _removeWatcher(sessionNum
, matchingKey
);
434 /* remove session from watchers */
435 pSessions
= (CFMutableArrayRef
)CFArrayGetValueAtIndex(pInfo
, 1);
436 n
= CFArrayGetCount(pSessions
);
438 /* if other sessions are watching this pattern */
440 pSessions
= CFArrayCreateMutableCopy(NULL
, 0, pSessions
);
441 i
= CFArrayGetFirstIndexOfValue(pSessions
, CFRangeMake(0, n
), sessionNum
);
442 CFArrayRemoveValueAtIndex(pSessions
, i
);
443 CFArraySetValueAtIndex(pInfo
, 1, pSessions
);
444 CFRelease(pSessions
);
446 CFDictionarySetValue(patternData
, pattern
, pInfo
);
448 /* if no other sessions are watching this pattern */
450 pRegex
= CFArrayGetValueAtIndex(pInfo
, 0);
451 patternRelease(pRegex
);
452 CFDictionaryRemoveValue(patternData
, pattern
);
461 addKeyForPattern(const void *key
, void *val
, void *context
)
463 CFStringRef pattern
= (CFStringRef
)key
;
464 CFArrayRef pInfo
= (CFArrayRef
)val
;
465 CFStringRef storeKey
= (CFStringRef
)context
;
473 /* convert store key to C string */
474 len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey
), kCFStringEncodingASCII
) + 1;
475 if (len
> (CFIndex
)sizeof(str_q
))
476 str
= CFAllocatorAllocate(NULL
, len
, 0);
477 if (_SC_cfstring_to_cstring(storeKey
, str
, len
, kCFStringEncodingASCII
) == NULL
) {
478 SC_log(LOG_INFO
, "could not convert store key to C string");
482 /* compare new store key to regular expression pattern */
483 /* ALIGN: CF aligns to >8 byte boundries */
484 preg
= (regex_t
*)(void *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo
, 0));
485 reError
= regexec(preg
, str
, 0, NULL
, 0);
493 CFMutableArrayRef pInfo_new
;
494 CFArrayRef pSessions
;
497 pSessions
= CFArrayGetValueAtIndex(pInfo
, 1);
498 n
= CFArrayGetCount(pSessions
);
499 for (i
= 0; i
< n
; i
++) {
500 CFNumberRef sessionNum
= CFArrayGetValueAtIndex(pSessions
, i
);
502 _addWatcher(sessionNum
, storeKey
);
505 /* add key, update pattern watcher info */
506 pInfo_new
= CFArrayCreateMutableCopy(NULL
, 0, pInfo
);
507 CFArrayAppendValue(pInfo_new
, storeKey
);
508 CFDictionarySetValue(patternData
, pattern
, pInfo_new
);
509 CFRelease(pInfo_new
);
518 (void)regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
519 SC_log(LOG_INFO
, "%s", reErrBuf
);
526 if (str
!= str_q
) CFAllocatorDeallocate(NULL
, str
);
533 patternAddKey(CFStringRef key
)
535 void *context
= (void *)key
;
537 my_CFDictionaryApplyFunction(patternData
,
538 (CFDictionaryApplierFunction
)addKeyForPattern
,
546 removeKeyFromPattern(const void *key
, void *val
, void *context
)
548 CFStringRef pattern
= (CFStringRef
)key
;
549 CFArrayRef pInfo
= (CFArrayRef
)val
;
550 CFStringRef storeKey
= (CFStringRef
)context
;
554 CFMutableArrayRef pInfo_new
;
555 CFArrayRef pSessions
;
557 n
= CFArrayGetCount(pInfo
);
559 /* if no keys match this pattern */
563 i
= CFArrayGetFirstIndexOfValue(pInfo
, CFRangeMake(2, n
-2), storeKey
);
564 if (i
== kCFNotFound
) {
565 /* if this key wasn't matched by this pattern */
569 /* remove key from pattern info */
570 pInfo_new
= CFArrayCreateMutableCopy(NULL
, 0, pInfo
);
571 CFArrayRemoveValueAtIndex(pInfo_new
, i
);
573 /* remove watchers */
574 pSessions
= CFArrayGetValueAtIndex(pInfo_new
, 1);
575 n
= CFArrayGetCount(pSessions
);
576 for (i
= 0; i
< n
; i
++) {
577 CFNumberRef sessionNum
= CFArrayGetValueAtIndex(pSessions
, i
);
579 _removeWatcher(sessionNum
, storeKey
);
582 CFDictionarySetValue(patternData
, pattern
, pInfo_new
);
583 CFRelease(pInfo_new
);
590 patternRemoveKey(CFStringRef key
)
592 void *context
= (void *)key
;
594 my_CFDictionaryApplyFunction(patternData
,
595 (CFDictionaryApplierFunction
)removeKeyFromPattern
,