Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
main.c
// |
// File: main.c |
// |
// Contains: CFPreferences by design only allows write access to the kCFPreferencesAnyUser domain by a user |
// with admin privileges. But occasionally developers have had the need to store user preferences |
// that are both readable and writable by all users (without authorization). Currently the only |
// location that meets this requirement is the </Users/Shared> directory. This sample demonstrates |
// how this may be achieved utilizing Core Foundation API's. |
// |
// Version: 1.0 |
// |
// Created: 6/27/2006 |
// |
// Copyright: Copyright 2006 Apple Computer, Inc., All Rights Reserved |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. |
// |
// **************************************************** |
#pragma mark - |
#pragma mark * complation directives * |
// ---------------------------------------------------- |
#define kMaxNumHighScores 10 |
// **************************************************** |
#pragma mark - |
#pragma mark * includes & imports * |
// ---------------------------------------------------- |
#include <Carbon/Carbon.h> |
#include <CoreFoundation/CFPreferences.h> |
#include <stdio.h> |
#include "CFPreferences_SharedAppValue.h" |
// **************************************************** |
#pragma mark - |
#pragma mark * typedef's, struct's, enums, defines, etc. * |
// ---------------------------------------------------- |
static const CFStringRef gCurrentApplicationID = CFSTR("com.apple.dts.sample.CFPrefTopScores"); |
#define kHighScoresKeyCFStr CFSTR( "High Scores" ) |
// **************************************************** |
#pragma mark - |
#pragma mark * local ( static ) function prototypes * |
// ---------------------------------------------------- |
static char * Copy_CFStringRefToCString( CFStringRef inCFStringRef ); |
static void HighScores_Dump( void ); |
static __inline__ int random_UInt32_between( UInt32 inStart, UInt32 inStop ); |
static Boolean HighScores_Update( const char * inPlayer, SInt32 inScore ); |
static Boolean HighScores_Reset( void ); |
// **************************************************** |
#pragma mark - |
#pragma mark * exported globals * |
// ---------------------------------------------------- |
// **************************************************** |
#pragma mark - |
#pragma mark * local ( static ) globals * |
// ---------------------------------------------------- |
// **************************************************** |
#pragma mark - |
#pragma mark * exported function implementations * |
// ---------------------------------------------------- |
// ***************************************************** |
// |
// Routine: main ( ) |
// |
// Purpose: Generates 100 random scores and attempts to add them to the high score preference |
// |
// Inputs: none |
// |
// Returns: int - error code ( 0 == no error ) |
// |
int main( void ) |
{ |
SInt32 idx; |
UInt32 score; |
Boolean again = TRUE; |
printf("Before: "); |
HighScores_Dump( ); |
while ( again ) { |
// generate one hundred random scores... |
for ( idx = 0; idx < 100; idx++ ) { |
Str255 playerString; |
sprintf( ( Ptr ) playerString, "Player #%.3ld", idx ); |
score = random_UInt32_between( 0, 100000 ); |
// update the high scores... |
// ... if our socre is a high score... |
if ( HighScores_Update( ( Ptr ) playerString, score ) ) { |
again = FALSE; // terminate outer loop |
} |
} |
// if we didn't generate any new high scores... |
if ( again ) { |
// ... reset the high scores list (pref) |
SysBeep( -1 ); |
printf( "Resetting the high scores list...\n" ); |
again = HighScores_Reset( ); |
} |
} |
printf("After: "); |
HighScores_Dump( ); |
return 0; |
} // main |
// **************************************************** |
#pragma mark - |
#pragma mark * local ( static ) function implementations * |
// ---------------------------------------------------- |
// ***************************************************** |
// |
// Routine: Copy_CFStringRefToCString ( inCFStringRef ) |
// |
// Purpose: return a C string from a CFString |
// |
// Note: If not NULL the result has to be free'd. |
// |
// Inputs: inCFStringRef - the CFString to extract the C string from |
// |
// Returns: char * - pointer to C string buffer or NULL on error |
// |
static char * Copy_CFStringRefToCString( CFStringRef inCFStringRef ) |
{ |
char * result = NULL; |
if ( inCFStringRef ) { |
CFIndex length = sizeof( UniChar ) * CFStringGetLength( inCFStringRef ) + 1; |
result = ( char * ) calloc( 1, length ); |
if ( result ) { |
if ( !CFStringGetCString( inCFStringRef, result, length, kCFStringEncodingASCII ) ) { |
if ( !CFStringGetCString( inCFStringRef, result, length, kCFStringEncodingUTF8 ) ) { |
free( result ); |
result = NULL; |
} |
} |
} |
} |
return result; |
} // Copy_CFStringRefToCString |
// ***************************************************** |
// |
// Routine: random_UInt32_between ( inStart, inStop ) |
// |
// Purpose: generate a random UInt32 between start & stop ( inclusive ) |
// |
// Inputs: inStart - UInt32 bottom of range |
// inStop - UInt32 top of range |
// |
// Returns: UInt32 - the random number |
// |
static __inline__ int random_UInt32_between( UInt32 inStart, UInt32 inStop ) |
{ |
UInt32 range = ( ( inStart < inStop ) ? ( inStop - inStart ) : ( inStart - inStop ) ) + 1; |
static UInt32 seed = 0; |
if ( !seed ) { // if the seed is zero... |
seed = CFAbsoluteTimeGetCurrent( ); |
srandom( seed ); |
} |
seed = range * ( ( float ) random( ) / ( float ) RAND_MAX ); |
return ( ( inStart < inStop ) ? inStart : inStop ) + seed; |
} // random_UInt32_between |
// ***************************************************** |
// |
// Routine: HighScores_Dump ( ) |
// |
// Purpose: dump the high scores (duh!) |
// |
// Inputs: none |
// |
// Returns: none |
// |
static void HighScores_Dump( void ) |
{ |
// get the current high scores |
CFArrayRef prefCFArrayRef = CFPreferences_CopySharedAppValue( kHighScoresKeyCFStr, gCurrentApplicationID ); |
if ( !prefCFArrayRef ) |
return; |
// how many did we get? |
CFIndex countHighScores, idx; |
countHighScores = CFArrayGetCount( prefCFArrayRef ); |
// print them out |
printf( "The high scores are:\n" ); |
for ( idx = 0;idx < countHighScores;idx++ ) { |
// get one of the high score elements |
CFArrayRef dataCFArrayRef; |
dataCFArrayRef = CFArrayGetValueAtIndex( prefCFArrayRef, idx ); |
if ( !dataCFArrayRef ) continue; |
// extract the players name... |
CFStringRef playerCFStringRef; |
playerCFStringRef = CFArrayGetValueAtIndex( dataCFArrayRef, 0 ); |
if ( !playerCFStringRef ) continue; |
char * playerStrPtr = Copy_CFStringRefToCString( playerCFStringRef ); |
if ( !playerStrPtr ) continue; |
// and their score |
SInt32 score = 0; |
CFNumberRef scoreCFNumberRef = CFArrayGetValueAtIndex( dataCFArrayRef, 1 ); |
if ( scoreCFNumberRef ) { |
if ( !CFNumberGetValue( scoreCFNumberRef, kCFNumberSInt32Type, &score ) ) { |
score = 0; |
} |
} |
// ... and the date of their hi score. |
CFDateRef tCFDateRef = NULL; |
if ( CFArrayGetCount( dataCFArrayRef ) >= 3 ) { |
tCFDateRef = CFArrayGetValueAtIndex( dataCFArrayRef, 2 ); |
} |
// if we got a date |
char * dateStrPtr = nil; |
if ( tCFDateRef ) { |
// make sure it's the write type... |
assert( CFGetTypeID( tCFDateRef ) == CFDateGetTypeID( ) ); |
// use this to convert to local date / time / format |
CFLocaleRef tCFLocaleRef = CFLocaleCopyCurrent( ); |
assert( tCFLocaleRef ); |
// create a local formatter |
CFDateFormatterRef tCFDateFormatterRef = CFDateFormatterCreate( kCFAllocatorDefault, tCFLocaleRef, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle ); |
assert( tCFDateFormatterRef ); |
CFRelease( tCFLocaleRef ); |
// now convert our CFDate( /time ) to a CFString ( using the formatter ) |
CFStringRef dateCFStringRef = CFDateFormatterCreateStringWithDate( kCFAllocatorDefault, tCFDateFormatterRef, tCFDateRef ); |
assert( dateCFStringRef ); |
CFRelease( tCFDateFormatterRef ); |
// and extract our C string from that |
dateStrPtr = Copy_CFStringRefToCString( dateCFStringRef ); |
CFRelease( dateCFStringRef ); |
} |
// if the player string is valid... |
if ( playerStrPtr ) { |
// ... and the date string is valid... |
if ( dateStrPtr ) { |
// ... print out the player, score and date |
printf( "\t%ld\t\"%s\"\t%ld\t%s\n", idx, playerStrPtr, score, dateStrPtr ); |
// and free our date string memory |
free( dateStrPtr ); |
} else { // ... otherwise ... |
// print out our player and score ( no date ) |
printf( "\t%ld\t\"%s\"\t%ld\n", idx, playerStrPtr, score ); |
} |
// free the player's string |
free( playerStrPtr ); |
} |
} |
CFRelease( prefCFArrayRef ); |
} // HighScores_Dump |
// ***************************************************** |
// |
// Routine: HighScores_Update ( inPlayer, inScore ) |
// |
// Purpose: update the saved high scores |
// |
// Inputs: inPlayer - char * pointer to players name |
// inScore - SInt32 score |
// |
// Returns: TRUE if it's a new high score |
// |
static Boolean HighScores_Update( const char * inPlayer, SInt32 inScore ) |
{ |
// get the high scores |
CFArrayRef prefCFArrayRef = CFPreferences_CopySharedAppValue( kHighScoresKeyCFStr, gCurrentApplicationID ); |
Boolean dirty = FALSE; |
// If prefs don't exist or aren't of the correct type... |
if ( !prefCFArrayRef || ( CFArrayGetTypeID() != CFGetTypeID( prefCFArrayRef ) ) ) { |
// ...create a new empty array for high scores |
prefCFArrayRef = CFArrayCreate( kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks ); |
dirty = TRUE; |
} |
// how many high scores are there? |
CFIndex countHighScores; |
countHighScores = CFArrayGetCount( prefCFArrayRef ); |
if ( countHighScores > kMaxNumHighScores ) { |
countHighScores = kMaxNumHighScores; |
} else if ( countHighScores < kMaxNumHighScores ) { |
dirty = TRUE; |
} |
CFArrayRef dataCFArrayRef = NULL; |
CFStringRef playerCFStringRef; // the player's name |
CFNumberRef scoreCFNumberRef; // the player's score |
// for each of the existing high scores |
UInt32 idx; |
for ( idx = 0; idx < countHighScores; idx++ ) { |
SInt32 score; |
// extract the high score data |
dataCFArrayRef = CFArrayGetValueAtIndex( prefCFArrayRef, idx ); |
if ( !dataCFArrayRef ) break; |
// extract the score |
scoreCFNumberRef = CFArrayGetValueAtIndex( dataCFArrayRef, 1 ); |
if ( !CFNumberGetValue( scoreCFNumberRef, kCFNumberSInt32Type, &score ) ) |
score = 0; |
if ( score < inScore ) // if our score is higher than this score... |
break; |
} |
// if dirty ( new array or count < max ) or our score is higher than a score in the list |
if ( dirty || ( idx < countHighScores ) ) { |
void * data[3]; |
// create player name & score CF types |
playerCFStringRef = CFStringCreateWithCString( kCFAllocatorDefault, inPlayer, kCFStringEncodingASCII ); |
scoreCFNumberRef = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &inScore ); |
// create current date / time |
CFAbsoluteTime tCFAbsoluteTime = CFAbsoluteTimeGetCurrent( ); |
CFDateRef tCFDateRef = CFDateCreate( kCFAllocatorDefault, tCFAbsoluteTime ); |
data[0] = ( void * ) playerCFStringRef; |
data[1] = ( void * ) scoreCFNumberRef; |
data[2] = ( void * ) tCFDateRef; |
// create an array of the name / score pair |
dataCFArrayRef = CFArrayCreate( kCFAllocatorDefault, ( void * ) data, 3, &kCFTypeArrayCallBacks ); |
CFRelease( playerCFStringRef ); |
CFRelease( scoreCFNumberRef ); |
CFRelease( tCFDateRef ); |
// create a mutable copy of our high score array |
CFMutableArrayRef tCFMutableArrayRef; |
tCFMutableArrayRef = CFArrayCreateMutableCopy( kCFAllocatorDefault, kMaxNumHighScores, prefCFArrayRef ); |
// If we're replacing the last entry delete it first |
if ( countHighScores == kMaxNumHighScores ) { |
CFArrayRemoveValueAtIndex( tCFMutableArrayRef, kMaxNumHighScores - 1 ); |
} |
// insert our new name / score / date pair into the prefs array |
CFArrayInsertValueAtIndex( tCFMutableArrayRef, idx, dataCFArrayRef ); |
CFRelease( dataCFArrayRef ); |
// now save it back into our prefs file |
dirty = CFPreferences_SetSharedAppValue( kHighScoresKeyCFStr, tCFMutableArrayRef, gCurrentApplicationID ); |
if ( !dirty ) { |
fprintf( stderr, "HighScores_Update: Preferences NOT saved.\n" ); fflush( stderr ); |
} |
#if FALSE |
// this is what doesn't work unless you have write priv's (admin?) to "/Library/Preferences" |
CFPreferencesSetValue( kHighScoresKeyCFStr, tCFMutableArrayRef, gCurrentApplicationID, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); |
if ( !CFPreferencesSynchronize( gCurrentApplicationID, kCFPreferencesAnyUser, kCFPreferencesCurrentHost) ) { |
fprintf( stderr, "\nHighScores_Update: Global CF Preferences NOT saved." ); fflush( stderr ); |
} |
#endif |
CFRelease( tCFMutableArrayRef ); |
} |
CFRelease( prefCFArrayRef ); |
return dirty; |
} // HighScores_Update |
// ***************************************************** |
// |
// Routine: HighScores_Reset ( ) |
// |
// Purpose: reset the saved high scores |
// |
// Inputs: none |
// |
// Returns: TRUE if successful |
// |
static Boolean HighScores_Reset( void ) |
{ |
// NULL is passed to delete this preference |
Boolean result = CFPreferences_SetSharedAppValue( kHighScoresKeyCFStr, NULL, gCurrentApplicationID ); |
#if FALSE |
CFPreferencesSetValue( kHighScoresKeyCFStr, NULL, gCurrentApplicationID, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); |
if ( !CFPreferencesSynchronize( gCurrentApplicationID, kCFPreferencesAnyUser, kCFPreferencesCurrentHost ) ) { |
fprintf( stderr, "HighScores_Reset: Global CF Preferences NOT saved.\n" ); fflush( stderr ); |
} |
#endif FALSE |
return result; |
} // HighScores_Reset |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-10-09