summaryrefslogtreecommitdiff
path: root/distrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'distrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp')
-rwxr-xr-xdistrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp686
1 files changed, 686 insertions, 0 deletions
diff --git a/distrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp b/distrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp
new file mode 100755
index 00000000..c88d8756
--- /dev/null
+++ b/distrho/src/CoreAudio106/PublicUtility/CASettingsStorage.cpp
@@ -0,0 +1,686 @@
+/* Copyright © 2007 Apple Inc. All Rights Reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by
+ Apple Inc. ("Apple") in consideration of your agreement to the
+ following terms, and your use, installation, modification or
+ redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use,
+ install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc.
+ may be used to endorse or promote products derived from the Apple
+ Software without specific prior written permission from Apple. Except
+ as expressly stated in this notice, no other rights or licenses, express
+ or implied, are granted by Apple herein, including but not limited to
+ any patent rights that may be infringed by your derivative works or by
+ other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+//==================================================================================================
+// Includes
+//==================================================================================================
+
+// Self Include
+#include "CASettingsStorage.h"
+
+// PublicUtility Includes
+#include "CAAutoDisposer.h"
+#include "CACFData.h"
+#include "CACFDistributedNotification.h"
+#include "CACFNumber.h"
+
+// Stamdard Library Includes
+#include <string.h>
+#include <sys/fcntl.h>
+
+//==================================================================================================
+// CASettingsStorage
+//==================================================================================================
+
+CASettingsStorage::CASettingsStorage(const char* inSettingsFilePath, mode_t inSettingsFileAccessMode)
+{
+ size_t theLength = strlen(inSettingsFilePath);
+ mSettingsFilePath = new char[theLength + 2];
+ strlcpy(mSettingsFilePath, inSettingsFilePath, theLength + 2);
+
+ mSettingsFileAccessMode = inSettingsFileAccessMode;
+
+ mSettingsCache = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ mSettingsCacheTime.tv_sec = 0;
+ mSettingsCacheTime.tv_nsec = 0;
+
+ mSettingsCacheForceRefresh = true;
+}
+
+CASettingsStorage::~CASettingsStorage()
+{
+ delete[] mSettingsFilePath;
+
+ if(mSettingsCache != NULL)
+ {
+ CFRelease(mSettingsCache);
+ }
+}
+
+void CASettingsStorage::CopyBoolValue(CFStringRef inKey, bool& outValue, bool inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // bools can be made from either CFBooleans or CFNumbers
+ if(CFGetTypeID(theValue) == CFBooleanGetTypeID())
+ {
+ // get the return value from the CF object
+ outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
+ }
+ else if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the numeric value
+ SInt32 theNumericValue = 0;
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
+
+ // non-zero indicates true
+ outValue = theNumericValue != 0;
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopySInt32Value(CFStringRef inKey, SInt32& outValue, SInt32 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopyUInt32Value(CFStringRef inKey, UInt32& outValue, UInt32 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopySInt64Value(CFStringRef inKey, SInt64& outValue, SInt64 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopyUInt64Value(CFStringRef inKey, UInt64& outValue, UInt64 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopyFloat32Value(CFStringRef inKey, Float32& outValue, Float32 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat32Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopyFloat64Value(CFStringRef inKey, Float64& outValue, Float64 inDefaultValue) const
+{
+ // initialize the return value
+ outValue = inDefaultValue;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, NULL);
+
+ // for this type, NULL is an invalid value
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // get the return value from the CF object
+ CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat64Type, &outValue);
+ }
+
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+ }
+}
+
+void CASettingsStorage::CopyNumberValue(CFStringRef inKey, CFNumberRef& outValue, CFNumberRef inDefaultValue) const
+{
+ // initialize the return value
+ outValue = NULL;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, inDefaultValue);
+
+ // for this type, NULL is a valid value, but requires less work
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFNumberGetTypeID())
+ {
+ // set the return value to the CF object we are returning
+ outValue = static_cast<CFNumberRef>(theValue);
+ }
+ else
+ {
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+
+ // set the return value to the default value
+ outValue = inDefaultValue;
+
+ // and retain it
+ CFRetain(outValue);
+ }
+ }
+}
+
+void CASettingsStorage::CopyStringValue(CFStringRef inKey, CFStringRef& outValue, CFStringRef inDefaultValue) const
+{
+ // initialize the return value
+ outValue = NULL;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, inDefaultValue);
+
+ // for this type, NULL is a valid value, but requires less work
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFStringGetTypeID())
+ {
+ // set the return value to the CF object we are returning
+ outValue = static_cast<CFStringRef>(theValue);
+ }
+ else
+ {
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+
+ // set the return value to the default value
+ outValue = inDefaultValue;
+
+ // and retain it
+ CFRetain(outValue);
+ }
+ }
+}
+
+void CASettingsStorage::CopyArrayValue(CFStringRef inKey, CFArrayRef& outValue, CFArrayRef inDefaultValue) const
+{
+ // initialize the return value
+ outValue = NULL;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, inDefaultValue);
+
+ // for this type, NULL is a valid value, but requires less work
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFArrayGetTypeID())
+ {
+ // set the return value to the CF object we are returning
+ outValue = static_cast<CFArrayRef>(theValue);
+ }
+ else
+ {
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+
+ // set the return value to the default value
+ outValue = inDefaultValue;
+
+ // and retain it
+ CFRetain(outValue);
+ }
+ }
+}
+
+void CASettingsStorage::CopyDictionaryValue(CFStringRef inKey, CFDictionaryRef& outValue, CFDictionaryRef inDefaultValue) const
+{
+ // initialize the return value
+ outValue = NULL;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, inDefaultValue);
+
+ // for this type, NULL is a valid value, but requires less work
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFDictionaryGetTypeID())
+ {
+ // set the return value to the CF object we are returning
+ outValue = static_cast<CFDictionaryRef>(theValue);
+ }
+ else
+ {
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+
+ // set the return value to the default value
+ outValue = inDefaultValue;
+
+ // and retain it
+ CFRetain(outValue);
+ }
+ }
+}
+
+void CASettingsStorage::CopyDataValue(CFStringRef inKey, CFDataRef& outValue, CFDataRef inDefaultValue) const
+{
+ // initialize the return value
+ outValue = NULL;
+
+ // get the raw value
+ CFTypeRef theValue = NULL;
+ CopyCFTypeValue(inKey, theValue, inDefaultValue);
+
+ // for this type, NULL is a valid value, but requires less work
+ if(theValue != NULL)
+ {
+ // make sure we are dealing with the right kind of CF object
+ if(CFGetTypeID(theValue) == CFDataGetTypeID())
+ {
+ // set the return value to the CF object we are returning
+ outValue = static_cast<CFDataRef>(theValue);
+ }
+ else
+ {
+ // release the value since we aren't returning it
+ CFRelease(theValue);
+
+ // set the return value to the default value
+ outValue = inDefaultValue;
+
+ // and retain it
+ CFRetain(outValue);
+ }
+ }
+}
+
+void CASettingsStorage::CopyCFTypeValue(CFStringRef inKey, CFTypeRef& outValue, CFTypeRef inDefaultValue) const
+{
+ // make sure our cache is up to date
+ const_cast<CASettingsStorage*>(this)->RefreshSettings();
+
+ // check to see if we have a value for the given key
+ if(!CFDictionaryGetValueIfPresent(mSettingsCache, inKey, &outValue))
+ {
+ // the key wasn't in the cache, so return the default value
+ outValue = inDefaultValue;
+ }
+
+ // make sure we retain the return value
+ if(outValue != NULL)
+ {
+ CFRetain(outValue);
+ }
+}
+
+void CASettingsStorage::SetSInt32Value(CFStringRef inKey, SInt32 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetUInt32Value(CFStringRef inKey, UInt32 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetSInt64Value(CFStringRef inKey, SInt64 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetUInt64Value(CFStringRef inKey, UInt64 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetFloat32Value(CFStringRef inKey, Float32 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetFloat64Value(CFStringRef inKey, Float64 inValue)
+{
+ CACFNumber theValue(inValue);
+ SetCFTypeValue(inKey, theValue.GetCFNumber());
+}
+
+void CASettingsStorage::SetNumberValue(CFStringRef inKey, CFNumberRef inValue)
+{
+ SetCFTypeValue(inKey, inValue);
+}
+
+void CASettingsStorage::SetStringValue(CFStringRef inKey, CFStringRef inValue)
+{
+ SetCFTypeValue(inKey, inValue);
+}
+
+void CASettingsStorage::SetArrayValue(CFStringRef inKey, CFArrayRef inValue)
+{
+ SetCFTypeValue(inKey, inValue);
+}
+
+void CASettingsStorage::SetDictionaryValue(CFStringRef inKey, CFDictionaryRef inValue)
+{
+ SetCFTypeValue(inKey, inValue);
+}
+
+void CASettingsStorage::SetDataValue(CFStringRef inKey, CFDataRef inValue)
+{
+ SetCFTypeValue(inKey, inValue);
+}
+
+void CASettingsStorage::SetCFTypeValue(CFStringRef inKey, CFTypeRef inValue)
+{
+ // make sure our cache is up to date
+ RefreshSettings();
+
+ // add the new key/value to the dictionary
+ CFDictionarySetValue(mSettingsCache, inKey, inValue);
+
+ // write the settings to the file
+ SaveSettings();
+}
+
+void CASettingsStorage::RemoveValue(CFStringRef inKey)
+{
+ // make sure our cache is up to date
+ RefreshSettings();
+
+ // remove the given key
+ CFDictionaryRemoveValue(mSettingsCache, inKey);
+
+ // write the settings to the file
+ SaveSettings();
+}
+
+void CASettingsStorage::RemoveAllValues()
+{
+ // make sure our cache is up to date
+ RefreshSettings();
+
+ // remove the given key
+ CFDictionaryRemoveAllValues(mSettingsCache);
+
+ // write the settings to the file
+ SaveSettings();
+}
+
+void CASettingsStorage::SendNotification(CFStringRef inName, CFDictionaryRef inData, bool inPostToAllSessions) const
+{
+ CACFDistributedNotification::PostNotification(inName, inData, inPostToAllSessions);
+}
+
+void CASettingsStorage::ForceRefresh()
+{
+ mSettingsCacheForceRefresh = true;
+}
+
+inline bool operator<(const struct timespec& inX, const struct timespec& inY)
+{
+ return ((inX.tv_sec < inY.tv_sec) || ((inX.tv_sec == inY.tv_sec) && (inX.tv_nsec < inY.tv_nsec)));
+}
+
+void CASettingsStorage::RefreshSettings()
+{
+ // first, we need to stat the file to check the mod date, this has the side effect of also
+ // telling us if the file exisits
+ struct stat theFileInfo;
+ int theStatError = stat(mSettingsFilePath, &theFileInfo);
+
+ // we use this boolean to make error recovery easier since we need a case for when there's no file anyway
+ bool theSettingsWereCached = false;
+
+ if(theStatError == 0)
+ {
+ // stat says there is something there, only have to do work if we either don't have a cache or the cache is out of date
+ if((mSettingsCache == NULL) || (mSettingsCacheTime < theFileInfo.st_mtimespec) || mSettingsCacheForceRefresh)
+ {
+ // open the file
+ FILE* theFile = fopen(mSettingsFilePath, "r");
+ if(theFile != NULL)
+ {
+ // lock the file (this call blocks until the lock is taken)
+ int theError = flock(fileno(theFile), LOCK_EX);
+ if(theError == 0)
+ {
+ // get the length of the file
+ fseek(theFile, 0, SEEK_END);
+ size_t theFileLength = ftell(theFile);
+ fseek(theFile, 0, SEEK_SET);
+
+ if(theFileLength > 0)
+ {
+ // allocate a block of memory to hold the data in the file
+ CAAutoFree<Byte> theRawFileData(theFileLength);
+
+ // read all the data in
+ fread(static_cast<Byte*>(theRawFileData), theFileLength, 1, theFile);
+
+ // release the lock
+ flock(fileno(theFile), LOCK_UN);
+
+ // put it into a CFData object
+ CACFData theRawFileDataCFData(static_cast<Byte*>(theRawFileData), static_cast<UInt32>(theFileLength));
+
+ // get rid of the existing cache
+ if(mSettingsCache != NULL)
+ {
+ CFRelease(mSettingsCache);
+ mSettingsCache = NULL;
+ }
+
+ // parse the data as a property list
+ mSettingsCache = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(NULL, theRawFileDataCFData.GetCFData(), kCFPropertyListMutableContainersAndLeaves, NULL);
+
+ // save the date of the cache
+ mSettingsCacheTime = theFileInfo.st_mtimespec;
+
+ // mark that we're done
+ theSettingsWereCached = true;
+ }
+ }
+
+ // close the file
+ fclose(theFile);
+ mSettingsCacheForceRefresh = false;
+ }
+ }
+ }
+
+ if((theStatError != 0) || (!theSettingsWereCached && (theFileInfo.st_mtimespec < mSettingsCacheTime)))
+ {
+ // we get here if either there isn't a file or something wacky happenned while parsing it
+ // all we do here is make sure that the member variables are set to their initial values
+ if(mSettingsCache != NULL)
+ {
+ CFRelease(mSettingsCache);
+ mSettingsCache = NULL;
+ }
+ mSettingsCache = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ mSettingsCacheTime.tv_sec = 0;
+ mSettingsCacheTime.tv_nsec = 0;
+ }
+}
+
+void CASettingsStorage::SaveSettings()
+{
+ if(mSettingsCache != NULL)
+ {
+ // make a CFData that contains the new settings
+ CACFData theNewRawPrefsCFData(CFPropertyListCreateXMLData(NULL, (CFPropertyListRef)mSettingsCache), true);
+
+ // open the file for writing
+ FILE* theFile = fopen(mSettingsFilePath, "w+");
+ if(theFile != NULL)
+ {
+ // lock the file (this call blocks until the lock is taken)
+ int theError = flock(fileno(theFile), LOCK_EX);
+ if(theError == 0)
+ {
+ // set the file access mode if necessary
+ if(mSettingsFileAccessMode != 0)
+ {
+ fchmod(fileno(theFile), mSettingsFileAccessMode);
+ }
+
+ // write the data
+ fwrite(theNewRawPrefsCFData.GetDataPtr(), theNewRawPrefsCFData.GetSize(), 1, theFile);
+
+ // flush the file to be sure it is all on disk
+ fflush(theFile);
+
+ // release the lock
+ flock(fileno(theFile), LOCK_UN);
+
+ // close the file
+ fclose(theFile);
+
+ // stat the file to get the mod date
+ struct stat theFileInfo;
+ stat(mSettingsFilePath, &theFileInfo);
+
+ // save the mod date
+ mSettingsCacheTime = theFileInfo.st_mtimespec;
+ }
+ else
+ {
+ // close the file
+ fclose(theFile);
+ }
+ }
+ }
+}