1
/****************************************************************************
2
**
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
14
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file.  Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights.  These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
**
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
**
31
**
32
**
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qsettings.h"
43
#include "qsettings_p.h"
44
#include "qdatetime.h"
45
#include "qdir.h"
46
#include "qvarlengtharray.h"
47
#include "private/qcore_mac_p.h"
48
49
QT_BEGIN_NAMESPACE
50
51
static const CFStringRef hostNames[2] = { kCFPreferencesCurrentHost, kCFPreferencesAnyHost };
52
static const int numHostNames = 2;
53
54
/*
55
    On the Mac, it is more natural to use '.' as the key separator
56
    than '/'. Therefore, it makes sense to replace '/' with '.' in
57
    keys. Then we replace '.' with middle dots (which we can't show
58
    here) and middle dots with '/'. A key like "4.0/BrowserCommand"
59
    becomes "4<middot>0.BrowserCommand".
60
*/
61
62
enum RotateShift { Macify = 1, Qtify = 2 };
63
64
static QString rotateSlashesDotsAndMiddots(const QString &key, int shift)
65
{
66
    static const int NumKnights = 3;
67
    static const char knightsOfTheRoundTable[NumKnights] = { '/', '.', '\xb7' };
68
    QString result = key;
69
70
    for (int i = 0; i < result.size(); ++i) {
71
        for (int j = 0; j < NumKnights; ++j) {
72
            if (result.at(i) == QLatin1Char(knightsOfTheRoundTable[j])) {
73
                result[i] = QLatin1Char(knightsOfTheRoundTable[(j + shift) % NumKnights]).unicode();
74
                break;
75
            }
76
        }
77
    }
78
    return result;
79
}
80
81
static QCFType<CFStringRef> macKey(const QString &key)
82
{
83
    return QCFString::toCFStringRef(rotateSlashesDotsAndMiddots(key, Macify));
84
}
85
86
static QString qtKey(CFStringRef cfkey)
87
{
88
    return rotateSlashesDotsAndMiddots(QCFString::toQString(cfkey), Qtify);
89
}
90
91
static QCFType<CFPropertyListRef> macValue(const QVariant &value);
92
93
static CFArrayRef macList(const QList<QVariant> &list)
94
{
95
    int n = list.size();
96
    QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(n);
97
    for (int i = 0; i < n; ++i)
98
        cfvalues[i] = macValue(list.at(i));
99
    return CFArrayCreate(kCFAllocatorDefault, reinterpret_cast<const void **>(cfvalues.data()),
100
                         CFIndex(n), &kCFTypeArrayCallBacks);
101
}
102
103
static QCFType<CFPropertyListRef> macValue(const QVariant &value)
104
{
105
    CFPropertyListRef result = 0;
106
107
    switch (value.type()) {
108
    case QVariant::ByteArray:
109
        {
110
            QByteArray ba = value.toByteArray();
111
            result = CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(ba.data()),
112
                                  CFIndex(ba.size()));
113
        }
114
        break;
115
    // should be same as below (look for LIST)
116
    case QVariant::List:
117
    case QVariant::StringList:
118
    case QVariant::Polygon:
119
        result = macList(value.toList());
120
        break;
121
    case QVariant::Map:
122
        {
123
            /*
124
                QMap<QString, QVariant> is potentially a multimap,
125
                whereas CFDictionary is a single-valued map. To allow
126
                for multiple values with the same key, we store
127
                multiple values in a CFArray. To avoid ambiguities,
128
                we also wrap lists in a CFArray singleton.
129
            */
130
            QMap<QString, QVariant> map = value.toMap();
131
            QMap<QString, QVariant>::const_iterator i = map.constBegin();
132
133
            int maxUniqueKeys = map.size();
134
            int numUniqueKeys = 0;
135
            QVarLengthArray<QCFType<CFPropertyListRef> > cfkeys(maxUniqueKeys);
136
            QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(maxUniqueKeys);
137
138
            while (i != map.constEnd()) {
139
                const QString &key = i.key();
140
                QList<QVariant> values;
141
142
                do {
143
                    values << i.value();
144
                    ++i;
145
                } while (i != map.constEnd() && i.key() == key);
146
147
                bool singleton = (values.count() == 1);
148
                if (singleton) {
149
                    switch (values.first().type()) {
150
                    // should be same as above (look for LIST)
151
                    case QVariant::List:
152
                    case QVariant::StringList:
153
                    case QVariant::Polygon:
154
                        singleton = false;
155
                    default:
156
                        ;
157
                    }
158
                }
159
160
                cfkeys[numUniqueKeys] = QCFString::toCFStringRef(key);
161
                cfvalues[numUniqueKeys] = singleton ? macValue(values.first()) : macList(values);
162
                ++numUniqueKeys;
163
            }
164
165
            result = CFDictionaryCreate(kCFAllocatorDefault,
166
                                        reinterpret_cast<const void **>(cfkeys.data()),
167
                                        reinterpret_cast<const void **>(cfvalues.data()),
168
                                        CFIndex(numUniqueKeys),
169
                                        &kCFTypeDictionaryKeyCallBacks,
170
                                        &kCFTypeDictionaryValueCallBacks);
171
        }
172
        break;
173
    case QVariant::DateTime:
174
        {
175
            /*
176
                CFDate, unlike QDateTime, doesn't store timezone information.
177
            */
178
            QDateTime dt = value.toDateTime();
179
            if (dt.timeSpec() == Qt::LocalTime) {
180
                QDateTime reference;
181
                reference.setTime_t((uint)kCFAbsoluteTimeIntervalSince1970);
182
                result = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTime(reference.secsTo(dt)));
183
            } else {
184
                goto string_case;
185
            }
186
        }
187
        break;
188
    case QVariant::Bool:
189
        result = value.toBool() ? kCFBooleanTrue : kCFBooleanFalse;
190
        break;
191
    case QVariant::Int:
192
    case QVariant::UInt:
193
        {
194
            int n = value.toInt();
195
            result = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n);
196
        }
197
        break;
198
    case QVariant::Double:
199
        {
200
            double n = value.toDouble();
201
            result = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &n);
202
        }
203
        break;
204
    case QVariant::LongLong:
205
    case QVariant::ULongLong:
206
        {
207
            qint64 n = value.toLongLong();
208
            result = CFNumberCreate(0, kCFNumberLongLongType, &n);
209
        }
210
        break;
211
    case QVariant::String:
212
    string_case:
213
    default:
214
        result = QCFString::toCFStringRef(QSettingsPrivate::variantToString(value));
215
    }
216
    return result;
217
}
218
219
static QVariant qtValue(CFPropertyListRef cfvalue)
220
{
221
    if (!cfvalue)
222
        return QVariant();
223
224
    CFTypeID typeId = CFGetTypeID(cfvalue);
225
226
    /*
227
        Sorted grossly from most to least frequent type.
228
    */
229
    if (typeId == CFStringGetTypeID()) {
230
        return QSettingsPrivate::stringToVariant(QCFString::toQString(static_cast<CFStringRef>(cfvalue)));
231
    } else if (typeId == CFNumberGetTypeID()) {
232
        CFNumberRef cfnumber = static_cast<CFNumberRef>(cfvalue);
233
        if (CFNumberIsFloatType(cfnumber)) {
234
            double d;
235
            CFNumberGetValue(cfnumber, kCFNumberDoubleType, &d);
236
            return d;
237
        } else {
238
            int i;
239
            qint64 ll;
240
241
            if (CFNumberGetValue(cfnumber, kCFNumberIntType, &i))
242
                return i;
243
            CFNumberGetValue(cfnumber, kCFNumberLongLongType, &ll);
244
            return ll;
245
        }
246
    } else if (typeId == CFArrayGetTypeID()) {
247
        CFArrayRef cfarray = static_cast<CFArrayRef>(cfvalue);
248
        QList<QVariant> list;
249
        CFIndex size = CFArrayGetCount(cfarray);
250
        bool metNonString = false;
251
        for (CFIndex i = 0; i < size; ++i) {
252
            QVariant value = qtValue(CFArrayGetValueAtIndex(cfarray, i));
253
            if (value.type() != QVariant::String)
254
                metNonString = true;
255
            list << value;
256
        }
257
        if (metNonString)
258
            return list;
259
        else
260
            return QVariant(list).toStringList();
261
    } else if (typeId == CFBooleanGetTypeID()) {
262
        return (bool)CFBooleanGetValue(static_cast<CFBooleanRef>(cfvalue));
263
    } else if (typeId == CFDataGetTypeID()) {
264
        CFDataRef cfdata = static_cast<CFDataRef>(cfvalue);
265
        return QByteArray(reinterpret_cast<const char *>(CFDataGetBytePtr(cfdata)),
266
                          CFDataGetLength(cfdata));
267
    } else if (typeId == CFDictionaryGetTypeID()) {
268
        CFDictionaryRef cfdict = static_cast<CFDictionaryRef>(cfvalue);
269
        CFTypeID arrayTypeId = CFArrayGetTypeID();
270
        int size = (int)CFDictionaryGetCount(cfdict);
271
        QVarLengthArray<CFPropertyListRef> keys(size);
272
        QVarLengthArray<CFPropertyListRef> values(size);
273
        CFDictionaryGetKeysAndValues(cfdict, keys.data(), values.data());
274
275
        QMultiMap<QString, QVariant> map;
276
        for (int i = 0; i < size; ++i) {
277
            QString key = QCFString::toQString(static_cast<CFStringRef>(keys[i]));
278
279
            if (CFGetTypeID(values[i]) == arrayTypeId) {
280
                CFArrayRef cfarray = static_cast<CFArrayRef>(values[i]);
281
                CFIndex arraySize = CFArrayGetCount(cfarray);
282
                for (CFIndex j = arraySize - 1; j >= 0; --j)
283
                    map.insert(key, qtValue(CFArrayGetValueAtIndex(cfarray, j)));
284
            } else {
285
                map.insert(key, qtValue(values[i]));
286
            }
287
        }
288
        return map;
289
    } else if (typeId == CFDateGetTypeID()) {
290
        QDateTime dt;
291
        dt.setTime_t((uint)kCFAbsoluteTimeIntervalSince1970);
292
        return dt.addSecs((int)CFDateGetAbsoluteTime(static_cast<CFDateRef>(cfvalue)));
293
    }
294
    return QVariant();
295
}
296
297
static QString comify(const QString &organization)
298
{
299
    for (int i = organization.size() - 1; i >= 0; --i) {
300
        QChar ch = organization.at(i);
301
        if (ch == QLatin1Char('.') || ch == QChar(0x3002) || ch == QChar(0xff0e)
302
                || ch == QChar(0xff61)) {
303
            QString suffix = organization.mid(i + 1).toLower();
304
            if (suffix.size() == 2 || suffix == QLatin1String("com")
305
                    || suffix == QLatin1String("org") || suffix == QLatin1String("net")
306
                    || suffix == QLatin1String("edu") || suffix == QLatin1String("gov")
307
                    || suffix == QLatin1String("mil") || suffix == QLatin1String("biz")
308
                    || suffix == QLatin1String("info") || suffix == QLatin1String("name")
309
                    || suffix == QLatin1String("pro") || suffix == QLatin1String("aero")
310
                    || suffix == QLatin1String("coop") || suffix == QLatin1String("museum")) {
311
                QString result = organization;
312
                result.replace(QLatin1Char('/'), QLatin1Char(' '));
313
                return result;
314
            }
315
            break;
316
        }
317
        int uc = ch.unicode();
318
        if ((uc < 'a' || uc > 'z') && (uc < 'A' || uc > 'Z'))
319
            break;
320
    }
321
322
    QString domain;
323
    for (int i = 0; i < organization.size(); ++i) {
324
        QChar ch = organization.at(i);
325
        int uc = ch.unicode();
326
        if ((uc >= 'a' && uc <= 'z') || (uc >= '0' && uc <= '9')) {
327
            domain += ch;
328
        } else if (uc >= 'A' && uc <= 'Z') {
329
            domain += ch.toLower();
330
        } else {
331
           domain += QLatin1Char(' ');
332
        }
333
    }
334
    domain = domain.simplified();
335
    domain.replace(QLatin1Char(' '), QLatin1Char('-'));
336
    if (!domain.isEmpty())
337
        domain.append(QLatin1String(".com"));
338
    return domain;
339
}
340
341
class QMacSettingsPrivate : public QSettingsPrivate
342
{
343
public:
344
    QMacSettingsPrivate(QSettings::Scope scope, const QString &organization,
345
                        const QString &application);
346
    ~QMacSettingsPrivate();
347
348
    void remove(const QString &key);
349
    void set(const QString &key, const QVariant &value);
350
    bool get(const QString &key, QVariant *value) const;
351
    QStringList children(const QString &prefix, ChildSpec spec) const;
352
    void clear();
353
    void sync();
354
    void flush();
355
    bool isWritable() const;
356
    QString fileName() const;
357
358
private:
359
    struct SearchDomain
360
    {
361
        CFStringRef userName;
362
        CFStringRef applicationOrSuiteId;
363
    };
364
365
    QCFString applicationId;
366
    QCFString suiteId;
367
    QCFString hostName;
368
    SearchDomain domains[6];
369
    int numDomains;
370
};
371
372
QMacSettingsPrivate::QMacSettingsPrivate(QSettings::Scope scope, const QString &organization,
373
                                         const QString &application)
374
    : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
375
{
376
    QString javaPackageName;
377
    int curPos = 0;
378
    int nextDot;
379
380
    QString domainName = comify(organization);
381
    if (domainName.isEmpty()) {
382
        setStatus(QSettings::AccessError);
383
        domainName = QLatin1String("unknown-organization.trolltech.com");
384
    }
385
386
    while ((nextDot = domainName.indexOf(QLatin1Char('.'), curPos)) != -1) {
387
        javaPackageName.prepend(domainName.mid(curPos, nextDot - curPos));
388
        javaPackageName.prepend(QLatin1Char('.'));
389
        curPos = nextDot + 1;
390
    }
391
    javaPackageName.prepend(domainName.mid(curPos));
392
    javaPackageName = javaPackageName.toLower();
393
    if (curPos == 0)
394
        javaPackageName.prepend(QLatin1String("com."));
395
    suiteId = javaPackageName;
396
397
    if (scope == QSettings::SystemScope)
398
        spec |= F_System;
399
400
    if (application.isEmpty()) {
401
        spec |= F_Organization;
402
    } else {
403
        javaPackageName += QLatin1Char('.');
404
        javaPackageName += application;
405
        applicationId = javaPackageName;
406
    }
407
408
    numDomains = 0;
409
    for (int i = (spec & F_System) ? 1 : 0; i < 2; ++i) {
410
        for (int j = (spec & F_Organization) ? 1 : 0; j < 3; ++j) {
411
            SearchDomain &domain = domains[numDomains++];
412
            domain.userName = (i == 0) ? kCFPreferencesCurrentUser : kCFPreferencesAnyUser;
413
            if (j == 0)
414
                domain.applicationOrSuiteId = applicationId;
415
            else if (j == 1)
416
                domain.applicationOrSuiteId = suiteId;
417
            else
418
                domain.applicationOrSuiteId = kCFPreferencesAnyApplication;
419
        }
420
    }
421
422
    hostName = (scope == QSettings::SystemScope) ? kCFPreferencesCurrentHost : kCFPreferencesAnyHost;
423
    sync();
424
}
425
426
QMacSettingsPrivate::~QMacSettingsPrivate()
427
{
428
}
429
430
void QMacSettingsPrivate::remove(const QString &key)
431
{
432
    QStringList keys = children(key + QLatin1Char('/'), AllKeys);
433
434
    // If i == -1, then delete "key" itself.
435
    for (int i = -1; i < keys.size(); ++i) {
436
        QString subKey = key;
437
        if (i >= 0) {
438
            subKey += QLatin1Char('/');
439
            subKey += keys.at(i);
440
        }
441
        CFPreferencesSetValue(macKey(subKey), 0, domains[0].applicationOrSuiteId,
442
                              domains[0].userName, hostName);
443
    }
444
}
445
446
void QMacSettingsPrivate::set(const QString &key, const QVariant &value)
447
{
448
    CFPreferencesSetValue(macKey(key), macValue(value), domains[0].applicationOrSuiteId,
449
                          domains[0].userName, hostName);
450
}
451
452
bool QMacSettingsPrivate::get(const QString &key, QVariant *value) const
453
{
454
    QCFString k = macKey(key);
455
    for (int i = 0; i < numDomains; ++i) {
456
        for (int j = 0; j < numHostNames; ++j) {
457
            QCFType<CFPropertyListRef> ret =
458
                    CFPreferencesCopyValue(k, domains[i].applicationOrSuiteId, domains[i].userName,
459
                                           hostNames[j]);
460
            if (ret) {
461
                if (value)
462
                    *value = qtValue(ret);
463
                return true;
464
            }
465
        }
466
467
        if (!fallbacks)
468
            break;
469
    }
470
    return false;
471
}
472
473
QStringList QMacSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
474
{
475
    QMap<QString, QString> result;
476
    int startPos = prefix.size();
477
478
    for (int i = 0; i < numDomains; ++i) {
479
        for (int j = 0; j < numHostNames; ++j) {
480
            QCFType<CFArrayRef> cfarray = CFPreferencesCopyKeyList(domains[i].applicationOrSuiteId,
481
                                                                   domains[i].userName,
482
                                                                   hostNames[j]);
483
            if (cfarray) {
484
                CFIndex size = CFArrayGetCount(cfarray);
485
                for (CFIndex k = 0; k < size; ++k) {
486
                    QString currentKey =
487
                            qtKey(static_cast<CFStringRef>(CFArrayGetValueAtIndex(cfarray, k)));
488
                    if (currentKey.startsWith(prefix))
489
                        processChild(currentKey.mid(startPos), spec, result);
490
                }
491
            }
492
        }
493
494
        if (!fallbacks)
495
            break;
496
    }
497
    return result.keys();
498
}
499
500
void QMacSettingsPrivate::clear()
501
{
502
    QCFType<CFArrayRef> cfarray = CFPreferencesCopyKeyList(domains[0].applicationOrSuiteId,
503
                                                           domains[0].userName, hostName);
504
    CFPreferencesSetMultiple(0, cfarray, domains[0].applicationOrSuiteId, domains[0].userName,
505
                             hostName);
506
}
507
508
void QMacSettingsPrivate::sync()
509
{
510
    for (int i = 0; i < numDomains; ++i) {
511
        for (int j = 0; j < numHostNames; ++j) {
512
            Boolean ok = CFPreferencesSynchronize(domains[i].applicationOrSuiteId,
513
                                                  domains[i].userName, hostNames[j]);
514
            // only report failures for the primary file (the one we write to)
515
            if (!ok && i == 0 && hostNames[j] == hostName && status == QSettings::NoError) {
516
#if 1
517
                // work around what seems to be a bug in CFPreferences:
518
                // don't report an error if there are no preferences for the application
519
                QCFType<CFArrayRef> appIds = CFPreferencesCopyApplicationList(domains[i].userName,
520
                                                                              hostNames[j]);
521
522
                // iterate through all the applications and see if we're there
523
                CFIndex size = CFArrayGetCount(appIds);
524
                for (CFIndex k = 0; k < size; ++k) {
525
                    const void *cfvalue = CFArrayGetValueAtIndex(appIds, k);
526
                    if (CFGetTypeID(cfvalue) == CFStringGetTypeID()) {
527
                        if (CFStringCompare(static_cast<CFStringRef>(cfvalue),
528
                                            domains[i].applicationOrSuiteId,
529
                                            kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
530
                            setStatus(QSettings::AccessError);
531
                            break;
532
                        }
533
                    }
534
                }
535
#else
536
                setStatus(QSettings::AccessError);
537
#endif
538
            }
539
        }
540
    }
541
}
542
543
void QMacSettingsPrivate::flush()
544
{
545
    sync();
546
}
547
548
bool QMacSettingsPrivate::isWritable() const
549
{
550
    QMacSettingsPrivate *that = const_cast<QMacSettingsPrivate *>(this);
551
    QString impossibleKey(QLatin1String("qt_internal/"));
552
553
    QSettings::Status oldStatus = that->status;
554
    that->status = QSettings::NoError;
555
556
    that->set(impossibleKey, QVariant());
557
    that->sync();
558
    bool writable = (status == QSettings::NoError) && that->get(impossibleKey, 0);
559
    that->remove(impossibleKey);
560
    that->sync();
561
562
    that->status = oldStatus;
563
    return writable;
564
}
565
566
QString QMacSettingsPrivate::fileName() const
567
{
568
    QString result;
569
    if ((spec & F_System) == 0)
570
        result = QDir::homePath();
571
    result += QLatin1String("/Library/Preferences/");
572
    result += QCFString::toQString(domains[0].applicationOrSuiteId);
573
    result += QLatin1String(".plist");
574
    return result;
575
}
576
577
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,
578
                                           QSettings::Scope scope,
579
                                           const QString &organization,
580
                                           const QString &application)
581
{
582
    if (format == QSettings::NativeFormat) {
583
        return new QMacSettingsPrivate(scope, organization, application);
584
    } else {
585
        return new QConfFileSettingsPrivate(format, scope, organization, application);
586
    }
587
}
588
589
static QCFType<CFURLRef> urlFromFileName(const QString &fileName)
590
{
591
    return CFURLCreateWithFileSystemPath(kCFAllocatorDefault, QCFString(fileName),
592
                                         kCFURLPOSIXPathStyle, false);
593
}
594
595
bool QConfFileSettingsPrivate::readPlistFile(const QString &fileName, ParsedSettingsMap *map) const
596
{
597
    QCFType<CFDataRef> resource;
598
    SInt32 code;
599
    if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, urlFromFileName(fileName),
600
                                                  &resource, 0, 0, &code))
601
        return false;
602
603
    QCFString errorStr;
604
    QCFType<CFPropertyListRef> propertyList =
605
            CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable,
606
                                            &errorStr);
607
608
    if (!propertyList)
609
        return true;
610
    if (CFGetTypeID(propertyList) != CFDictionaryGetTypeID())
611
        return false;
612
613
    CFDictionaryRef cfdict =
614
            static_cast<CFDictionaryRef>(static_cast<CFPropertyListRef>(propertyList));
615
    int size = (int)CFDictionaryGetCount(cfdict);
616
    QVarLengthArray<CFPropertyListRef> keys(size);
617
    QVarLengthArray<CFPropertyListRef> values(size);
618
    CFDictionaryGetKeysAndValues(cfdict, keys.data(), values.data());
619
620
    for (int i = 0; i < size; ++i) {
621
        QString key = qtKey(static_cast<CFStringRef>(keys[i]));
622
        map->insert(QSettingsKey(key, Qt::CaseSensitive), qtValue(values[i]));
623
    }
624
    return true;
625
}
626
627
bool QConfFileSettingsPrivate::writePlistFile(const QString &fileName,
628
                                              const ParsedSettingsMap &map) const
629
{
630
    QVarLengthArray<QCFType<CFStringRef> > cfkeys(map.size());
631
    QVarLengthArray<QCFType<CFPropertyListRef> > cfvalues(map.size());
632
    int i = 0;
633
    ParsedSettingsMap::const_iterator j;
634
    for (j = map.constBegin(); j != map.constEnd(); ++j) {
635
        cfkeys[i] = macKey(j.key());
636
        cfvalues[i] = macValue(j.value());
637
        ++i;
638
    }
639
640
    QCFType<CFDictionaryRef> propertyList =
641
            CFDictionaryCreate(kCFAllocatorDefault,
642
                               reinterpret_cast<const void **>(cfkeys.data()),
643
                               reinterpret_cast<const void **>(cfvalues.data()),
644
                               CFIndex(map.size()),
645
                               &kCFTypeDictionaryKeyCallBacks,
646
                               &kCFTypeDictionaryValueCallBacks);
647
648
    QCFType<CFDataRef> xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, propertyList);
649
650
    SInt32 code;
651
    return CFURLWriteDataAndPropertiesToResource(urlFromFileName(fileName), xmlData, 0, &code);
652
}
653
654
QT_END_NAMESPACE