1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 
  33 
  34 #include "Platform.h"
  35 
  36 #ifdef MAC
  37 
  38 #include "MacPlatform.h"
  39 #include "Helpers.h"
  40 #include "Package.h"
  41 #include "PropertyFile.h"
  42 #include "IniFile.h"
  43 
  44 #include <sys/sysctl.h>
  45 #include <pthread.h>
  46 #include <vector>
  47 
  48 #import <Foundation/Foundation.h>
  49 
  50 #include <CoreFoundation/CoreFoundation.h>
  51 #include <CoreFoundation/CFString.h>
  52 
  53 #ifdef __OBJC__
  54 #import <Cocoa/Cocoa.h>
  55 #endif //__OBJC__
  56 
  57 //--------------------------------------------------------------------------------------------------
  58 
  59 NSString* StringToNSString(TString Value) {
  60     NSString* result = [NSString stringWithCString:Value.c_str()
  61                                           encoding:[NSString defaultCStringEncoding]];
  62     return result;
  63 }
  64 
  65 //--------------------------------------------------------------------------------------------------
  66 
  67 MacPlatform::MacPlatform(void) : Platform(), GenericPlatform(), PosixPlatform() {
  68 }
  69 
  70 MacPlatform::~MacPlatform(void) {
  71 }
  72 
  73 bool MacPlatform::UsePListForConfigFile() {
  74     return FilePath::FileExists(GetConfigFileName()) == false;
  75 }
  76 
  77 void MacPlatform::ShowMessage(TString Title, TString Description) {
  78     NSString *ltitle = StringToNSString(Title);
  79     NSString *ldescription = StringToNSString(Description);
  80 
  81     NSLog(@"%@:%@", ltitle, ldescription);
  82 }
  83 
  84 void MacPlatform::ShowMessage(TString Description) {
  85     TString appname = GetModuleFileName();
  86     appname = FilePath::ExtractFileName(appname);
  87     ShowMessage(appname, Description);
  88 }
  89 
  90 
  91 TCHAR* MacPlatform::ConvertStringToFileSystemString(TCHAR* Source, bool &release) {
  92     TCHAR* result = NULL;
  93     release = false;
  94     CFStringRef StringRef = CFStringCreateWithCString(kCFAllocatorDefault, Source, kCFStringEncodingUTF8);
  95 
  96     if (StringRef != NULL) {
  97         @try {
  98             CFIndex length = CFStringGetMaximumSizeOfFileSystemRepresentation(StringRef);
  99             result = new char[length + 1];
 100 
 101             if (CFStringGetFileSystemRepresentation(StringRef, result, length)) {
 102                 release = true;
 103             }
 104             else {
 105                 delete[] result;
 106                 result = NULL;
 107             }
 108         }
 109         @finally {
 110             CFRelease(StringRef);
 111         }
 112     }
 113 
 114     return result;
 115 }
 116 
 117 TCHAR* MacPlatform::ConvertFileSystemStringToString(TCHAR* Source, bool &release) {
 118     TCHAR* result = NULL;
 119     release = false;
 120     CFStringRef StringRef = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, Source);
 121 
 122     if (StringRef != NULL) {
 123         @try {
 124             CFIndex length = CFStringGetLength(StringRef);
 125 
 126             if (length > 0) {
 127                 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
 128 
 129                 result = new char[maxSize + 1];
 130 
 131                 if (CFStringGetCString(StringRef, result, maxSize, kCFStringEncodingUTF8) == true) {
 132                     release = true;
 133                 }
 134                 else {
 135                     delete[] result;
 136                     result = NULL;
 137                 }
 138             }
 139         }
 140         @finally {
 141             CFRelease(StringRef);
 142         }
 143     }
 144 
 145     return result;
 146 }
 147 
 148 void MacPlatform::SetCurrentDirectory(TString Value) {
 149     chdir(PlatformString(Value).toPlatformString());
 150 }
 151 
 152 TString MacPlatform::GetPackageRootDirectory() {
 153     NSBundle *mainBundle = [NSBundle mainBundle];
 154     NSString *mainBundlePath = [mainBundle bundlePath];
 155     NSString *contentsPath = [mainBundlePath stringByAppendingString:@"/Contents"];
 156     TString result = [contentsPath UTF8String];
 157     return result;
 158 }
 159 
 160 TString MacPlatform::GetAppDataDirectory() {
 161     TString result;
 162     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
 163     NSString *applicationSupportDirectory = [paths firstObject];
 164     result = [applicationSupportDirectory UTF8String];
 165     return result;
 166 }
 167 
 168 TString MacPlatform::GetBundledJVMLibraryFileName(TString RuntimePath) {
 169     TString result;
 170 
 171     result = FilePath::IncludeTrailingSeparater(RuntimePath) + _T("Contents/Home/jre/lib/jli/libjli.dylib");
 172 
 173     if (FilePath::FileExists(result) == false) {
 174         result = FilePath::IncludeTrailingSeparater(RuntimePath) + _T("Contents/Home/lib/jli/libjli.dylib");
 175 
 176         if (FilePath::FileExists(result) == false) {
 177             result = _T("");
 178         }
 179     }
 180 
 181     return result;
 182 }
 183 
 184 
 185 TString MacPlatform::GetSystemJRE() {
 186     if (GetAppCDSState() == cdsOn || GetAppCDSState() == cdsGenCache) {
 187         //TODO throw exception
 188         return _T("");
 189     }
 190 
 191     return _T("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/jli/libjli.dylib");
 192 }
 193 
 194 TString MacPlatform::GetSystemJVMLibraryFileName() {
 195     TString result = GetSystemJRE();
 196 
 197     if (FilePath::FileExists(result) == false) {
 198         result = _T("");
 199     }
 200 
 201     return result;
 202 }
 203 
 204 TString MacPlatform::GetAppName() {
 205     NSString *appName = [[NSProcessInfo processInfo] processName];
 206     TString result = [appName UTF8String];
 207     return result;
 208 }
 209 
 210 void AppendPListArrayToIniFile(NSDictionary *infoDictionary, IniFile *result, TString Section) {
 211     NSString *sectionKey = [NSString stringWithUTF8String:PlatformString(Section).toMultibyte()];
 212     NSDictionary *array = [infoDictionary objectForKey:sectionKey];
 213 
 214     for (id option in array) {
 215         if ([option isKindOfClass:[NSString class]]) {
 216             TString arg = [option UTF8String];
 217 
 218             TString name;
 219             TString value;
 220 
 221             if (Helpers::SplitOptionIntoNameValue(arg, name, value) == true) {
 222                 result->Append(Section, name, value);
 223             }
 224         }
 225     }
 226 }
 227 
 228 void AppendPListDictionaryToIniFile(NSDictionary *infoDictionary, IniFile *result, TString Section, bool FollowSection = true) {
 229     NSDictionary *dictionary = NULL;
 230 
 231     if (FollowSection == true) {
 232         NSString *sectionKey = [NSString stringWithUTF8String:PlatformString(Section).toMultibyte()];
 233         dictionary = [infoDictionary objectForKey:sectionKey];
 234     }
 235     else {
 236         dictionary = infoDictionary;
 237     }
 238 
 239     for (id key in dictionary) {
 240         id option = [dictionary valueForKey:key];
 241 
 242         if ([key isKindOfClass:[NSString class]] && [option isKindOfClass:[NSString class]]) {
 243             TString name = [key UTF8String];
 244             TString value = [option UTF8String];
 245             result->Append(Section, name, value);
 246         }
 247     }
 248 }
 249 
 250 // Convert parts of the info.plist to the INI format the rest of the packager uses unless
 251 // a packager config file exists.
 252 ISectionalPropertyContainer* MacPlatform::GetConfigFile(TString FileName) {
 253     IniFile* result = new IniFile();
 254 
 255     if (UsePListForConfigFile() == false) {
 256         if (result->LoadFromFile(FileName) == false) {
 257             // New property file format was not found, attempt to load old property file format.
 258             Helpers::LoadOldConfigFile(FileName, result);
 259         }
 260     }
 261     else {
 262         NSBundle *mainBundle = [NSBundle mainBundle];
 263         NSDictionary *infoDictionary = [mainBundle infoDictionary];
 264         std::map<TString, TString> keys = GetKeys();
 265 
 266         // Packager options.
 267         AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPLICATION], false);
 268 
 269         // jvmargs
 270         AppendPListArrayToIniFile(infoDictionary, result, keys[CONFIG_SECTION_JVMOPTIONS]);
 271 
 272         // jvmuserargs
 273         AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_JVMUSEROPTIONS]);
 274 
 275         // Generate AppCDS Cache
 276         AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPCDSJVMOPTIONS]);
 277         AppendPListDictionaryToIniFile(infoDictionary, result, keys[CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS]);
 278 
 279         // args
 280         AppendPListArrayToIniFile(infoDictionary, result, keys[CONFIG_SECTION_ARGOPTIONS]);
 281     }
 282 
 283     return result;
 284 }
 285 /*
 286 #include <mach-o/dyld.h>
 287 #include <mach-o/getsect.h>
 288 #include <dlfcn.h>
 289 #include <string>
 290 
 291 std::string GetModuleFileName(const void *module)
 292 {
 293     if (!module) { return std::string(); }
 294     uint32_t count = _dyld_image_count();
 295     for (uint32_t i = 0; i < count; ++i) {
 296         const mach_header *header = _dyld_get_image_header(i);
 297         if (!header) { break; }
 298         char *code_ptr = NULL;
 299         if ((header->magic & MH_MAGIC_64) == MH_MAGIC_64) {
 300             uint64_t size;
 301             code_ptr = getsectdatafromheader_64((const mach_header_64 *)header, SEG_TEXT, SECT_TEXT, &size);
 302         } else {
 303             uint32_t size;
 304             code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size);
 305         }
 306         if (!code_ptr) { continue; }
 307         const uintptr_t slide = _dyld_get_image_vmaddr_slide(i);
 308         const uintptr_t start = (const uintptr_t)code_ptr + slide;
 309         Dl_info info;
 310         if (dladdr((const void *)start, &info)) {
 311             if (dlopen(info.dli_fname, RTLD_NOW) == module) {
 312                 return std::string(info.dli_fname);
 313             }
 314         }
 315     }
 316     return std::string();
 317 }*/
 318 
 319 TString GetModuleFileNameOSX() {
 320     Dl_info module_info;
 321     if (dladdr(reinterpret_cast<void*>(GetModuleFileNameOSX), &module_info) == 0) {
 322         // Failed to find the symbol we asked for.
 323         return std::string();
 324     }
 325     return TString(module_info.dli_fname);
 326 }
 327 
 328 #include <mach-o/dyld.h>
 329 
 330 TString MacPlatform::GetModuleFileName() {
 331     //return GetModuleFileNameOSX();
 332 
 333     TString result;
 334     DynamicBuffer<TCHAR> buffer(MAX_PATH);
 335     uint32_t size = buffer.GetSize();
 336 
 337     if (_NSGetExecutablePath(buffer.GetData(), &size) == 0) {
 338         result = FileSystemStringToString(buffer.GetData());
 339     }
 340 
 341     return result;
 342 }
 343 
 344 bool MacPlatform::IsMainThread() {
 345     bool result = (pthread_main_np() == 1);
 346     return result;
 347 }
 348 
 349 TPlatformNumber MacPlatform::GetMemorySize() {
 350     unsigned long long memory = [[NSProcessInfo processInfo] physicalMemory];
 351     TPlatformNumber result = memory / 1048576; // Convert from bytes to megabytes.
 352     return result;
 353 }
 354 
 355 std::map<TString, TString> MacPlatform::GetKeys() {
 356     std::map<TString, TString> keys;
 357 
 358     if (UsePListForConfigFile() == false) {
 359         return GenericPlatform::GetKeys();
 360     }
 361     else {
 362         keys.insert(std::map<TString, TString>::value_type(CONFIG_VERSION,            _T("app.version")));
 363         keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINJAR_KEY,        _T("JVMMainJarName")));
 364         keys.insert(std::map<TString, TString>::value_type(CONFIG_MAINCLASSNAME_KEY,  _T("JVMMainClassName")));
 365         keys.insert(std::map<TString, TString>::value_type(CONFIG_CLASSPATH_KEY,      _T("JVMAppClasspath")));
 366         keys.insert(std::map<TString, TString>::value_type(APP_NAME_KEY,              _T("CFBundleName")));
 367         keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_ID_KEY,         _T("JVMPreferencesID")));
 368         keys.insert(std::map<TString, TString>::value_type(JVM_RUNTIME_KEY,           _T("JVMRuntime")));
 369         keys.insert(std::map<TString, TString>::value_type(PACKAGER_APP_DATA_DIR,     _T("CFBundleIdentifier")));
 370 
 371         keys.insert(std::map<TString, TString>::value_type(CONFIG_SPLASH_KEY,         _T("app.splash")));
 372         keys.insert(std::map<TString, TString>::value_type(CONFIG_APP_MEMORY,         _T("app.memory")));
 373 
 374         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPLICATION,    _T("Application")));
 375         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMOPTIONS,     _T("JVMOptions")));
 376         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMUSEROPTIONS, _T("JVMUserOptions")));
 377         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_JVMUSEROVERRIDESOPTIONS, _T("JVMUserOverrideOptions")));
 378         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPCDSJVMOPTIONS, _T("AppCDSJVMOptions")));
 379         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_APPCDSGENERATECACHEJVMOPTIONS, _T("AppCDSGenerateCacheJVMOptions")));
 380         keys.insert(std::map<TString, TString>::value_type(CONFIG_SECTION_ARGOPTIONS,     _T("ArgOptions")));
 381     }
 382 
 383     return keys;
 384 }
 385 
 386 #ifdef DEBUG
 387 bool MacPlatform::IsNativeDebuggerPresent() {
 388     int state;
 389     int mib[4];
 390     struct kinfo_proc info;
 391     size_t size;
 392 
 393     info.kp_proc.p_flag = 0;
 394 
 395     mib[0] = CTL_KERN;
 396     mib[1] = KERN_PROC;
 397     mib[2] = KERN_PROC_PID;
 398     mib[3] = getpid();
 399 
 400     size = sizeof(info);
 401     state = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
 402     assert(state == 0);
 403     return ((info.kp_proc.p_flag & P_TRACED) != 0);
 404 }
 405 
 406 int MacPlatform::GetProcessID() {
 407     int pid = [[NSProcessInfo processInfo] processIdentifier];
 408     return pid;
 409 }
 410 #endif //DEBUG
 411 
 412 //--------------------------------------------------------------------------------------------------
 413 
 414 class UserDefaults {
 415 private:
 416     OrderedMap<TString, TString> FData;
 417     TString FDomainName;
 418 
 419     bool ReadDictionary(NSDictionary *Items, OrderedMap<TString, TString> &Data) {
 420         bool result = false;
 421 
 422         for (id key in Items) {
 423             id option = [Items valueForKey:key];
 424 
 425             if ([key isKindOfClass:[NSString class]] && [option isKindOfClass:[NSString class]]) {
 426                 TString name = [key UTF8String];
 427                 TString value = [option UTF8String];
 428 
 429                 if (name.empty() == false) {
 430                     Data.Append(name, value);
 431                 }
 432             }
 433         }
 434 
 435         return result;
 436     }
 437 
 438     // Open and read the defaults file specified by domain.
 439     bool ReadPreferences(NSDictionary *Defaults, std::list<TString> Keys, OrderedMap<TString, TString> &Data) {
 440         bool result = false;
 441 
 442         if (Keys.size() > 0 && Defaults != NULL) {
 443             NSDictionary *node = Defaults;
 444 
 445             while (Keys.size() > 0 && node != NULL) {
 446                 TString key = Keys.front();
 447                 Keys.pop_front();
 448                 NSString *tempKey = StringToNSString(key);
 449                 node = [node valueForKey:tempKey];
 450 
 451                 if (Keys.size() == 0) {
 452                     break;
 453                 }
 454             }
 455 
 456             if (node != NULL) {
 457                 result = ReadDictionary(node, Data);
 458             }
 459         }
 460 
 461         return result;
 462     }
 463 
 464     NSDictionary* LoadPreferences(TString DomainName) {
 465         NSDictionary *result = NULL;
 466 
 467         if (DomainName.empty() == false) {
 468             NSUserDefaults *prefs = [[NSUserDefaults alloc] init];
 469 
 470             if (prefs != NULL) {
 471                 NSString *lDomainName = StringToNSString(DomainName);
 472                 result = [prefs persistentDomainForName: lDomainName];
 473             }
 474         }
 475 
 476         return result;
 477     }
 478 
 479 public:
 480     UserDefaults(TString DomainName) {
 481         FDomainName = DomainName;
 482     }
 483 
 484     bool Read(std::list<TString> Keys) {
 485         NSDictionary *defaults = LoadPreferences(FDomainName);
 486         return ReadPreferences(defaults, Keys, FData);
 487     }
 488 
 489     OrderedMap<TString, TString> GetData() {
 490         return FData;
 491     }
 492 };
 493 
 494 //--------------------------------------------------------------------------------------------------
 495 
 496 MacJavaUserPreferences::MacJavaUserPreferences(void) : JavaUserPreferences() {
 497 }
 498 
 499 TString toLowerCase(TString Value) {
 500     // Use Cocoa's lowercase method because it is better than the ones provided by C/C++.
 501     NSString *temp = StringToNSString(Value);
 502     temp = [temp lowercaseString];
 503     TString result = [temp UTF8String];
 504     return result;
 505 }
 506 
 507 // Split the string Value into using Delimiter.
 508 std::list<TString> Split(TString Value, TString Delimiter) {
 509     std::list<TString> result;
 510     std::vector<char> buffer(Value.c_str(), Value.c_str() + Value.size() + 1);
 511     char *p = strtok(&buffer[0], Delimiter.data());
 512 
 513     while (p != NULL) {
 514         TString token = p;
 515         result.push_back(token);
 516         p = strtok(NULL, Delimiter.data());
 517     }
 518 
 519     return result;
 520 }
 521 
 522 // 1. If the path is fewer than three components (Example: one/two/three) then the domain is the
 523 //    default domain "com.apple.java.util.prefs" stored in the plist file
 524 //    ~/Library/Preferences/com.apple.java.util.prefs.plist
 525 //
 526 //    For example: If AppID = "hello", the path is "hello/JVMUserOptions and the
 527 //    plist file is ~/Library/Preferences/com.apple.java.util.prefs.plist containing the contents:
 528 //
 529 //    <?xml version="1.0" encoding="UTF-8"?>
 530 //    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 531 //    <plist version="1.0">
 532 //    <dict>
 533 //      <key>/</key>
 534 //      <dict>
 535 //        <key>hello/</key>
 536 //        <dict>
 537 //          <key>JVMUserOptions/</key>
 538 //          <dict>
 539 //            <key>-DXmx</key>
 540 //            <string>512m</string>
 541 //          </dict>
 542 //        </dict>
 543 //      </dict>
 544 //    </dict>
 545 //    </plist>
 546 //
 547 // 2. If the path is three or more, the first three become the domain name (even
 548 //    if shared across applicaitons) and the remaining become individual keys.
 549 //
 550 //    For example: If AppID = "com/hello/foo", the path is "hello/JVMUserOptions and the
 551 //    domain is "com.hello.foo" stored in the plist file ~/Library/Preferences/com.hello.foo.plist
 552 //    containing the contents:
 553 //
 554 //    <?xml version="1.0" encoding="UTF-8"?>
 555 //    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 556 //    <plist version="1.0">
 557 //    <dict>
 558 //      <key>/com/hello/foo/</key>
 559 //      <dict>
 560 //        <key>JVMUserOptions/</key>
 561 //        <dict>
 562 //          <key>-DXmx</key>
 563 //          <string>512m</string>
 564 //        </dict>
 565 //      </dict>
 566 //    </dict>
 567 //    </plist>
 568 //
 569 // NOTE: To change these values use the command line utility "defaults":
 570 // Example: defaults read com.apple.java.util.prefs /
 571 // Since OS 10.9 Mavericks the defaults are cashed so directly modifying the files is not recommended.
 572 bool MacJavaUserPreferences::Load(TString Appid) {
 573     bool result = false;
 574 
 575     if (Appid.empty() == false) {
 576         // This is for backwards compatability. Older packaged applications have an
 577         // app.preferences.id that is delimited by period (".") rather than
 578         // slash ("/") so convert to newer style.
 579         TString path = Helpers::ReplaceString(Appid, _T("."), _T("/"));
 580 
 581         path = path + _T("/JVMUserOptions");
 582         TString domainName;
 583         std::list<TString> keys = Split(path, _T("/"));
 584 
 585         // If there are less than three parts to the path then use the default preferences file.
 586         if (keys.size() < 3) {
 587             domainName = _T("com.apple.java.util.prefs");
 588 
 589             // Append slash to the end of each key.
 590             for (std::list<TString>::iterator iterator = keys.begin(); iterator != keys.end(); iterator++) {
 591                 TString item = *iterator;
 592                 item = item + _T("/");
 593                 *iterator = item;
 594             }
 595 
 596             // The root key is /.
 597             keys.push_front(_T("/"));
 598         }
 599         else {
 600             // Remove the first three keys and use them for the root key and the preferencesID.
 601             TString one = keys.front();
 602             keys.pop_front();
 603             TString two = keys.front();
 604             keys.pop_front();
 605             TString three = keys.front();
 606             keys.pop_front();
 607             domainName = one + TString(".") + two + TString(".") + three;
 608             domainName = toLowerCase(domainName);
 609 
 610             // Append slash to the end of each key.
 611             for (std::list<TString>::iterator iterator = keys.begin(); iterator != keys.end(); iterator++) {
 612                 TString item = *iterator;
 613                 item = item + _T("/");
 614                 *iterator = item;
 615             }
 616 
 617             // The root key is /one/two/three/
 618             TString key = TString("/") + one + TString("/") + two + TString("/") + three + TString("/");
 619             keys.push_front(key);
 620         }
 621 
 622         UserDefaults userDefaults(domainName);
 623 
 624         if (userDefaults.Read(keys) == true) {
 625             result = true;
 626             FMap = userDefaults.GetData();
 627         }
 628     }
 629 
 630     return result;
 631 }
 632 
 633 //--------------------------------------------------------------------------------------------------
 634 
 635 #endif //MAC