X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/32d96e3d77d900203b7faba2d7937f8b3472f4d7..009ee3c6fe2929a4c90ae5c9eb1925573e17956b:/configd.tproj/pattern.c diff --git a/configd.tproj/pattern.c b/configd.tproj/pattern.c new file mode 100644 index 0000000..444fb86 --- /dev/null +++ b/configd.tproj/pattern.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Modification History + * + * April 23, 2003 Allan Nathanson + * - initial revision + */ + + +#include "configd.h" +#include "pattern.h" + + +/* + * Notes: + * + * - pattern matching information is maintained in a dictionary (patternData). + * - the dictionary "key" is the regular expression being matched + * - the dictionary "value" is a CFArray with the following contents + * [0] = CFData consisting of the pre-compiled regular expression + * [1] = CFArray[CFNumber] consisting of the sessions watching this pattern + * [2-n] = dynamic store keys which match this pattern + */ + + +typedef struct { + CFMutableArrayRef pInfo; + regex_t *preg; +} addContext, *addContextRef; + + +static __inline__ void +my_CFDictionaryApplyFunction(CFDictionaryRef theDict, + CFDictionaryApplierFunction applier, + void *context) +{ + CFAllocatorRef myAllocator; + CFDictionaryRef myDict; + + myAllocator = CFGetAllocator(theDict); + myDict = CFDictionaryCreateCopy(myAllocator, theDict); + CFDictionaryApplyFunction(myDict, applier, context); + CFRelease(myDict); + return; +} + + +static void +identifyKeyForPattern(const void *key, void *val, void *context) +{ + CFStringRef storeKey = (CFStringRef)key; + CFDictionaryRef storeValue = (CFDictionaryRef)val; + CFMutableArrayRef pInfo = ((addContextRef)context)->pInfo; + regex_t * preg = ((addContextRef)context)->preg; + + CFIndex len; + int reError; + char str_q[256]; + char * str = str_q; + + if (!CFDictionaryContainsKey(storeValue, kSCDData)) { + /* if no data (yet) */ + return; + } + + /* convert store key to C string */ + len = CFStringGetLength(storeKey) + 1; + if (len > (CFIndex)sizeof(str_q)) + str = CFAllocatorAllocate(NULL, len, 0); + if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) { + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("could not convert store key to C string")); + goto done; + } + + /* compare store key to new notification keys regular expression pattern */ + reError = regexec(preg, str, 0, NULL, 0); + switch (reError) { + case 0 : + /* we've got a match */ + CFArrayAppendValue(pInfo, storeKey); + break; + case REG_NOMATCH : + /* no match */ + break; + default : { + char reErrBuf[256]; + + (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); + break; + } + } + + done : + + if (str != str_q) CFAllocatorDeallocate(NULL, str); + return; +} + + +__private_extern__ Boolean +patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error) +{ + Boolean append = FALSE; + Boolean insert = FALSE; + CFIndex len = 0; + Boolean ok; + char str_q[256]; + char * str = str_q; + + if (!CFStringHasPrefix(pattern, CFSTR("^"))) { + insert = TRUE; + } + + if (!CFStringHasSuffix(pattern, CFSTR("$")) || + CFStringHasSuffix(pattern, CFSTR("\\$"))) { + append = TRUE; + } + + /* if regex pattern is not bounded at both ends */ + if (insert || append) { + pattern = CFStringCreateWithFormat(NULL, + NULL, + CFSTR("%s%@%s"), + insert ? "^" : "", + pattern, + append ? "$" : ""); + } + + (void)CFStringGetBytes(pattern, + CFRangeMake(0, CFStringGetLength(pattern)), + kCFStringEncodingASCII, + 0, + FALSE, + NULL, + 0, + &len); + if (++len > (CFIndex)sizeof(str_q)) { + str = CFAllocatorAllocate(NULL, len, 0); + } + ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL); + if (insert || append) { + CFRelease(pattern); + } + if (ok) { + int reError; + + reError = regcomp(preg, str, REG_EXTENDED); + if (reError != 0) { + char reErrBuf[256]; + + (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); + *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII); + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regcomp(%s) failed: %s"), str, reErrBuf); + ok = FALSE; + } + } else { + *error = CFRetain(CFSTR("could not convert pattern to regex string")); + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error); + } + + if (str != str_q) CFAllocatorDeallocate(NULL, str); + return ok; +} + + +__private_extern__ +CFMutableArrayRef +patternCopy(CFStringRef pattern) +{ + CFArrayRef pInfo; + + pInfo = CFDictionaryGetValue(patternData, pattern); + return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL; +} + + +__private_extern__ +CFMutableArrayRef +patternNew(CFStringRef pattern) +{ + addContext context; + CFStringRef err; + CFMutableArrayRef pInfo; + CFMutableDataRef pRegex; + CFArrayRef pSessions; + + /* create the pattern info */ + pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + /* compile the regular expression from the pattern string. */ + pRegex = CFDataCreateMutable(NULL, sizeof(regex_t)); + CFDataSetLength(pRegex, sizeof(regex_t)); + if (!patternCompile(pattern, (regex_t *)CFDataGetBytePtr(pRegex), &err)) { + CFRelease(err); + CFRelease(pRegex); + CFRelease(pInfo); + return NULL; + } + + /* add the compiled regular expression to the pattern info */ + CFArrayAppendValue(pInfo, pRegex); + + /* add the initial (empty) list of sessions watching this pattern */ + pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(pInfo, pSessions); + CFRelease(pSessions); + + /* identify/add all existing keys that match the specified pattern */ + context.pInfo = pInfo; + context.preg = (regex_t *)CFDataGetBytePtr(pRegex); + my_CFDictionaryApplyFunction(storeData, + (CFDictionaryApplierFunction)identifyKeyForPattern, + &context); + + CFRelease(pRegex); + return pInfo; +} + + +__private_extern__ +Boolean +patternAddSession(CFStringRef pattern, CFNumberRef sessionNum) +{ + CFIndex i; + CFIndex n; + CFMutableArrayRef pInfo; + CFMutableArrayRef pSessions; + + /* find (or create new instance of) this pattern */ + pInfo = patternCopy(pattern); + if (!pInfo) { + /* if new pattern */ + pInfo = patternNew(pattern); + if (!pInfo) { + return FALSE; + } + } + + /* add this session as a pattern watcher */ + pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1); + pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions); + CFArrayAppendValue(pSessions, sessionNum); + CFArraySetValueAtIndex(pInfo, 1, pSessions); + CFRelease(pSessions); + + /* update pattern watcher info */ + CFDictionarySetValue(patternData, pattern, pInfo); + + /* add this session as a watcher of any existing keys */ + n = CFArrayGetCount(pInfo); + for (i = 2; i < n; i++) { + CFStringRef matchingKey; + + matchingKey = CFArrayGetValueAtIndex(pInfo, i); + _addWatcher(sessionNum, matchingKey); + } + + CFRelease(pInfo); + return TRUE; +} + + +__private_extern__ +void +patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum) +{ + CFIndex i; + CFIndex n; + CFMutableArrayRef pInfo; + CFDataRef pRegex; + CFMutableArrayRef pSessions; + + /* find instance of this pattern */ + pInfo = patternCopy(pattern); + + /* remove this session as a watcher from all matching keys */ + n = CFArrayGetCount(pInfo); + for (i = 2; i < n; i++) { + CFStringRef matchingKey; + + matchingKey = CFArrayGetValueAtIndex(pInfo, i); + _removeWatcher(sessionNum, matchingKey); + } + + /* remove session from watchers */ + pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1); + n = CFArrayGetCount(pSessions); + if (n > 1) { + /* if other sessions are watching this pattern */ + + pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions); + i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum); + CFArrayRemoveValueAtIndex(pSessions, i); + CFArraySetValueAtIndex(pInfo, 1, pSessions); + CFRelease(pSessions); + + CFDictionarySetValue(patternData, pattern, pInfo); + } else { + /* if no other sessions are watching this pattern */ + + pRegex = CFArrayGetValueAtIndex(pInfo, 0); + regfree((regex_t *)CFDataGetBytePtr(pRegex)); + CFDictionaryRemoveValue(patternData, pattern); + } + + CFRelease(pInfo); + return; +} + + +static void +addKeyForPattern(const void *key, void *val, void *context) +{ + CFStringRef pattern = (CFStringRef)key; + CFArrayRef pInfo = (CFArrayRef)val; + CFStringRef storeKey = (CFStringRef)context; + + CFIndex len; + regex_t *preg; + int reError; + char str_q[256]; + char * str = str_q; + + /* convert store key to C string */ + len = CFStringGetLength(storeKey) + 1; + if (len > (CFIndex)sizeof(str_q)) + str = CFAllocatorAllocate(NULL, len, 0); + if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) { + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("could not convert store key to C string")); + goto done; + } + + /* compare new store key to regular expression pattern */ + preg = (regex_t *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0)); + reError = regexec(preg, str, 0, NULL, 0); + switch (reError) { + case 0 : { + /* + * we've got a match + */ + CFIndex i; + CFIndex n; + CFMutableArrayRef pInfo_new; + CFArrayRef pSessions; + + /* add watchers */ + pSessions = CFArrayGetValueAtIndex(pInfo, 1); + n = CFArrayGetCount(pSessions); + for (i = 0; i < n; i++) { + CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i); + + _addWatcher(sessionNum, storeKey); + } + + /* add key, update pattern watcher info */ + pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo); + CFArrayAppendValue(pInfo_new, storeKey); + CFDictionarySetValue(patternData, pattern, pInfo_new); + CFRelease(pInfo_new); + break; + } + case REG_NOMATCH : + /* no match */ + break; + default : { + char reErrBuf[256]; + + (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); + SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); + break; + } + } + + done : + + if (str != str_q) CFAllocatorDeallocate(NULL, str); + return; +} + + +__private_extern__ +void +patternAddKey(CFStringRef key) +{ + my_CFDictionaryApplyFunction(patternData, + (CFDictionaryApplierFunction)addKeyForPattern, + (void *)key); + + return; +} + + +static void +removeKeyFromPattern(const void *key, void *val, void *context) +{ + CFStringRef pattern = (CFStringRef)key; + CFArrayRef pInfo = (CFArrayRef)val; + CFStringRef storeKey = (CFStringRef)context; + + CFIndex i; + CFIndex n; + CFMutableArrayRef pInfo_new; + CFArrayRef pSessions; + + n = CFArrayGetCount(pInfo); + if (n <= 2) { + /* if no keys match this pattern */ + return; + } + + i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey); + if (i == -1) { + /* if this key wasn't matched by this pattern */ + return; + } + + /* remove key from pattern info */ + pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo); + CFArrayRemoveValueAtIndex(pInfo_new, i); + + /* remove watchers */ + pSessions = CFArrayGetValueAtIndex(pInfo_new, 1); + n = CFArrayGetCount(pSessions); + for (i = 0; i < n; i++) { + CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i); + + _removeWatcher(sessionNum, storeKey); + } + + CFDictionarySetValue(patternData, pattern, pInfo_new); + CFRelease(pInfo_new); + return; +} + + +__private_extern__ +void +patternRemoveKey(CFStringRef key) +{ + my_CFDictionaryApplyFunction(patternData, + (CFDictionaryApplierFunction)removeKeyFromPattern, + (void *)key); + + return; +}