]> git.saurik.com Git - apple/configd.git/blobdiff - configd.tproj/pattern.c
configd-84.tar.gz
[apple/configd.git] / configd.tproj / pattern.c
diff --git a/configd.tproj/pattern.c b/configd.tproj/pattern.c
new file mode 100644 (file)
index 0000000..444fb86
--- /dev/null
@@ -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 <ajn@apple.com>
+ * - 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;
+}