profile.c 59.1 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5
/*
 * Profile functions
 *
 * Copyright 1993 Miguel de Icaza
 * Copyright 1996 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

22
#include "config.h"
23
#include "wine/port.h"
24

Alexandre Julliard's avatar
Alexandre Julliard committed
25
#include <string.h>
26
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
27

28
#include "windef.h"
29
#include "winbase.h"
30
#include "winnls.h"
31
#include "winerror.h"
32
#include "winternl.h"
33
#include "wine/winbase16.h"
34
#include "wine/unicode.h"
35 36
#include "wine/library.h"
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
37

38
WINE_DEFAULT_DEBUG_CHANNEL(profile);
39

40 41 42 43 44 45 46 47 48 49
static const char bom_utf8[] = {0xEF,0xBB,0xBF};

typedef enum
{
    ENCODING_ANSI = 1,
    ENCODING_UTF8,
    ENCODING_UTF16LE,
    ENCODING_UTF16BE
} ENCODING;

Alexandre Julliard's avatar
Alexandre Julliard committed
50 51
typedef struct tagPROFILEKEY
{
52
    WCHAR                 *value;
Alexandre Julliard's avatar
Alexandre Julliard committed
53
    struct tagPROFILEKEY  *next;
54
    WCHAR                  name[1];
Alexandre Julliard's avatar
Alexandre Julliard committed
55 56 57 58 59 60
} PROFILEKEY;

typedef struct tagPROFILESECTION
{
    struct tagPROFILEKEY       *key;
    struct tagPROFILESECTION   *next;
61
    WCHAR                       name[1];
62
} PROFILESECTION;
Alexandre Julliard's avatar
Alexandre Julliard committed
63 64 65 66


typedef struct
{
67
    BOOL             changed;
Alexandre Julliard's avatar
Alexandre Julliard committed
68
    PROFILESECTION  *section;
69
    WCHAR           *filename;
70 71
    FILETIME LastWriteTime;
    ENCODING encoding;
Alexandre Julliard's avatar
Alexandre Julliard committed
72 73 74
} PROFILE;


Alexandre Julliard's avatar
Alexandre Julliard committed
75 76 77 78 79 80
#define N_CACHED_PROFILES 10

/* Cached profile files */
static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};

#define CurProfile (MRUProfile[0])
Alexandre Julliard's avatar
Alexandre Julliard committed
81 82 83 84

/* Check for comments in profile */
#define IS_ENTRY_COMMENT(str)  ((str)[0] == ';')

85
static const WCHAR emptystringW[] = {0};
86
static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
Alexandre Julliard's avatar
Alexandre Julliard committed
87

88 89 90 91 92
static CRITICAL_SECTION PROFILE_CritSect;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
    0, 0, &PROFILE_CritSect,
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
93
      0, 0, { (DWORD_PTR)(__FILE__ ": PROFILE_CritSect") }
94 95
};
static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
96

97
static const char hex[16] = "0123456789ABCDEF";
98

Alexandre Julliard's avatar
Alexandre Julliard committed
99 100 101 102 103 104
/***********************************************************************
 *           PROFILE_CopyEntry
 *
 * Copy the content of an entry into a buffer, removing quotes, and possibly
 * translating environment variables.
 */
105
static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
106
                               BOOL strip_quote )
Alexandre Julliard's avatar
Alexandre Julliard committed
107
{
108
    WCHAR quote = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
109

110 111
    if(!buffer) return;

112
    if (strip_quote && ((*value == '\'') || (*value == '\"')))
Alexandre Julliard's avatar
Alexandre Julliard committed
113
    {
114
        if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
Alexandre Julliard's avatar
Alexandre Julliard committed
115 116
    }

117
    lstrcpynW( buffer, value, len );
118
    if (quote && (len >= lstrlenW(value))) buffer[strlenW(buffer)-1] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
119 120
}

121 122 123 124
/* byte-swaps shorts in-place in a buffer. len is in WCHARs */
static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
{
    int i;
125
    USHORT * shortbuffer = buffer;
126 127 128 129 130 131 132 133
    for (i = 0; i < len; i++)
        shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
}

/* writes any necessary encoding marker to the file */
static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
{
    DWORD dwBytesWritten;
134
    WCHAR bom;
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    switch (encoding)
    {
    case ENCODING_ANSI:
        break;
    case ENCODING_UTF8:
        WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
        break;
    case ENCODING_UTF16LE:
        bom = 0xFEFF;
        WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
        break;
    case ENCODING_UTF16BE:
        bom = 0xFFFE;
        WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
        break;
    }
}

static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
{
155 156
    char * write_buffer;
    int write_buffer_len;
157 158 159 160 161 162 163
    DWORD dwBytesWritten;

    TRACE("writing: %s\n", debugstr_wn(szLine, len));

    switch (encoding)
    {
    case ENCODING_ANSI:
164 165 166 167 168 169
        write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
        write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
        if (!write_buffer) return;
        len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
        WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
        HeapFree(GetProcessHeap(), 0, write_buffer);
170 171
        break;
    case ENCODING_UTF8:
172 173 174 175 176 177
        write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
        write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
        if (!write_buffer) return;
        len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
        WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
        HeapFree(GetProcessHeap(), 0, write_buffer);
178 179 180 181 182 183 184 185 186 187 188 189
        break;
    case ENCODING_UTF16LE:
        WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
        break;
    case ENCODING_UTF16BE:
        PROFILE_ByteSwapShortBuffer(szLine, len);
        WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
        break;
    default:
        FIXME("encoding type %d not implemented\n", encoding);
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
190 191 192 193 194 195

/***********************************************************************
 *           PROFILE_Save
 *
 * Save a profile tree to a file.
 */
196
static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
Alexandre Julliard's avatar
Alexandre Julliard committed
197 198
{
    PROFILEKEY *key;
199 200
    WCHAR *buffer, *p;

201
    PROFILE_WriteMarker(hFile, encoding);
Alexandre Julliard's avatar
Alexandre Julliard committed
202 203 204

    for ( ; section; section = section->next)
    {
205
        int len = 0;
206

207
        if (section->name[0]) len += strlenW(section->name) + 4;
208 209

        for (key = section->key; key; key = key->next)
210
        {
211 212
            len += strlenW(key->name) + 2;
            if (key->value) len += strlenW(key->value) + 1;
213 214 215 216
        }

        buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        if (!buffer) return;
217

218 219 220
        p = buffer;
        if (section->name[0])
        {
221
            *p++ = '[';
222 223
            strcpyW( p, section->name );
            p += strlenW(p);
224 225 226
            *p++ = ']';
            *p++ = '\r';
            *p++ = '\n';
227
        }
228

Alexandre Julliard's avatar
Alexandre Julliard committed
229 230
        for (key = section->key; key; key = key->next)
        {
231 232
            strcpyW( p, key->name );
            p += strlenW(p);
233
            if (key->value)
234 235 236 237 238 239 240
            {
                *p++ = '=';
                strcpyW( p, key->value );
                p += strlenW(p);
            }
            *p++ = '\r';
            *p++ = '\n';
Alexandre Julliard's avatar
Alexandre Julliard committed
241
        }
242 243
        PROFILE_WriteLine( hFile, buffer, len, encoding );
        HeapFree(GetProcessHeap(), 0, buffer);
Alexandre Julliard's avatar
Alexandre Julliard committed
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    }
}


/***********************************************************************
 *           PROFILE_Free
 *
 * Free a profile tree.
 */
static void PROFILE_Free( PROFILESECTION *section )
{
    PROFILESECTION *next_section;
    PROFILEKEY *key, *next_key;

    for ( ; section; section = next_section)
    {
        for (key = section->key; key; key = next_key)
        {
            next_key = key->next;
263
            HeapFree( GetProcessHeap(), 0, key->value );
264
            HeapFree( GetProcessHeap(), 0, key );
Alexandre Julliard's avatar
Alexandre Julliard committed
265 266
        }
        next_section = section->next;
267
        HeapFree( GetProcessHeap(), 0, section );
Alexandre Julliard's avatar
Alexandre Julliard committed
268 269 270
    }
}

271 272
/* returns 1 if a character white space else 0 */
static inline int PROFILE_isspaceW(WCHAR c)
273
{
274 275
	/* ^Z (DOS EOF) is a space too  (found on CD-ROMs) */
	return isspaceW(c) || c == 0x1a;
Alexandre Julliard's avatar
Alexandre Julliard committed
276 277
}

278 279
static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
{
280 281 282
    int flags = IS_TEXT_UNICODE_SIGNATURE |
                IS_TEXT_UNICODE_REVERSE_SIGNATURE |
                IS_TEXT_UNICODE_ODD_LENGTH;
283 284 285 286 287
    if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
    {
        *len = sizeof(bom_utf8);
        return ENCODING_UTF8;
    }
288
    RtlIsTextUnicode(buffer, *len, &flags);
289 290 291 292 293 294 295 296 297 298 299 300 301 302
    if (flags & IS_TEXT_UNICODE_SIGNATURE)
    {
        *len = sizeof(WCHAR);
        return ENCODING_UTF16LE;
    }
    if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
    {
        *len = sizeof(WCHAR);
        return ENCODING_UTF16BE;
    }
    *len = 0;
    return ENCODING_ANSI;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
303 304 305 306 307 308

/***********************************************************************
 *           PROFILE_Load
 *
 * Load a profile tree from a file.
 */
309
static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
Alexandre Julliard's avatar
Alexandre Julliard committed
310
{
311
    void *buffer_base, *pBuffer;
312 313
    WCHAR * szFile;
    const WCHAR *szLineStart, *szLineEnd;
314
    const WCHAR *szValueStart, *szEnd, *next_line;
315
    int line = 0, len;
Alexandre Julliard's avatar
Alexandre Julliard committed
316
    PROFILESECTION *section, *first_section;
Alexandre Julliard's avatar
Alexandre Julliard committed
317 318
    PROFILESECTION **next_section;
    PROFILEKEY *key, *prev_key, **next_key;
319 320 321 322 323
    DWORD dwFileSize;
    
    TRACE("%p\n", hFile);
    
    dwFileSize = GetFileSize(hFile, NULL);
324
    if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
325 326
        return NULL;

327 328
    buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
    if (!buffer_base) return NULL;
329
    
330
    if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
331
    {
332
        HeapFree(GetProcessHeap(), 0, buffer_base);
333
        WARN("Error %d reading file\n", GetLastError());
334 335 336
        return NULL;
    }
    len = dwFileSize;
337
    *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
338 339
    /* len is set to the number of bytes in the character marker.
     * we want to skip these bytes */
340
    pBuffer = (char *)buffer_base + len;
341 342 343 344 345 346
    dwFileSize -= len;
    switch (*pEncoding)
    {
    case ENCODING_ANSI:
        TRACE("ANSI encoding\n");

347
        len = MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, NULL, 0);
348 349 350
        szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        if (!szFile)
        {
351
            HeapFree(GetProcessHeap(), 0, buffer_base);
352 353
            return NULL;
        }
354
        MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, szFile, len);
355 356 357 358
        szEnd = szFile + len;
        break;
    case ENCODING_UTF8:
        TRACE("UTF8 encoding\n");
359 360

        len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
361 362 363
        szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        if (!szFile)
        {
364
            HeapFree(GetProcessHeap(), 0, buffer_base);
365 366
            return NULL;
        }
367
        MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
368 369 370 371
        szEnd = szFile + len;
        break;
    case ENCODING_UTF16LE:
        TRACE("UTF16 Little Endian encoding\n");
372
        szFile = pBuffer;
373 374 375 376
        szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
        break;
    case ENCODING_UTF16BE:
        TRACE("UTF16 Big Endian encoding\n");
377
        szFile = pBuffer;
378 379 380 381 382
        szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
        PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
        break;
    default:
        FIXME("encoding type %d not implemented\n", *pEncoding);
383
        HeapFree(GetProcessHeap(), 0, buffer_base);
384 385
        return NULL;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
386

387
    first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
388 389 390 391
    if(first_section == NULL)
    {
        if (szFile != pBuffer)
            HeapFree(GetProcessHeap(), 0, szFile);
392
        HeapFree(GetProcessHeap(), 0, buffer_base);
393 394
        return NULL;
    }
395
    first_section->name[0] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
396 397
    first_section->key  = NULL;
    first_section->next = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
398 399 400
    next_section = &first_section->next;
    next_key     = &first_section->key;
    prev_key     = NULL;
401
    next_line    = szFile;
Alexandre Julliard's avatar
Alexandre Julliard committed
402

403
    while (next_line < szEnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
404
    {
405 406
        szLineStart = next_line;
        next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
407
        if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart);
408 409 410 411
        if (!next_line) next_line = szEnd;
        else next_line++;
        szLineEnd = next_line;

Alexandre Julliard's avatar
Alexandre Julliard committed
412
        line++;
413 414

        /* get rid of white space */
415
        while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
416
        while ((szLineEnd > szLineStart) && PROFILE_isspaceW(szLineEnd[-1])) szLineEnd--;
417

418 419 420
        if (szLineStart >= szLineEnd) continue;

        if (*szLineStart == '[')  /* section start */
Alexandre Julliard's avatar
Alexandre Julliard committed
421
        {
422
            const WCHAR * szSectionEnd;
423
            if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
Alexandre Julliard's avatar
Alexandre Julliard committed
424
            {
425 426
                WARN("Invalid section header at line %d: %s\n",
                    line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
Alexandre Julliard's avatar
Alexandre Julliard committed
427 428 429
            }
            else
            {
430 431 432 433
                szLineStart++;
                len = (int)(szSectionEnd - szLineStart);
                /* no need to allocate +1 for NULL terminating character as
                 * already included in structure */
434
                if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
435
                    break;
436 437
                memcpy(section->name, szLineStart, len * sizeof(WCHAR));
                section->name[len] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
438 439
                section->key  = NULL;
                section->next = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
440 441 442 443
                *next_section = section;
                next_section  = &section->next;
                next_key      = &section->key;
                prev_key      = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
444

445
                TRACE("New section: %s\n", debugstr_w(section->name));
Alexandre Julliard's avatar
Alexandre Julliard committed
446

Alexandre Julliard's avatar
Alexandre Julliard committed
447 448 449
                continue;
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
450

451 452
        /* get rid of white space after the name and before the start
         * of the value */
453 454
        len = szLineEnd - szLineStart;
        if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
Alexandre Julliard's avatar
Alexandre Julliard committed
455
        {
456 457 458
            const WCHAR *szNameEnd = szValueStart;
            while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
            len = szNameEnd - szLineStart;
459 460
            szValueStart++;
            while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
Alexandre Julliard's avatar
Alexandre Julliard committed
461
        }
462 463

        if (len || !prev_key || *prev_key->name)
464
        {
465 466
            /* no need to allocate +1 for NULL terminating character as
             * already included in structure */
467
            if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
468 469
            memcpy(key->name, szLineStart, len * sizeof(WCHAR));
            key->name[len] = '\0';
470
            if (szValueStart)
471
            {
472 473 474 475
                len = (int)(szLineEnd - szValueStart);
                key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
                memcpy(key->value, szValueStart, len * sizeof(WCHAR));
                key->value[len] = '\0';
476 477 478
            }
            else key->value = NULL;

Alexandre Julliard's avatar
Alexandre Julliard committed
479 480 481 482 483
           key->next  = NULL;
           *next_key  = key;
           next_key   = &key->next;
           prev_key   = key;

484 485
           TRACE("New key: name=%s, value=%s\n",
               debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
486
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
487
    }
488 489
    if (szFile != pBuffer)
        HeapFree(GetProcessHeap(), 0, szFile);
490
    HeapFree(GetProcessHeap(), 0, buffer_base);
Alexandre Julliard's avatar
Alexandre Julliard committed
491 492 493
    return first_section;
}

494

Alexandre Julliard's avatar
Alexandre Julliard committed
495 496 497 498 499
/***********************************************************************
 *           PROFILE_DeleteSection
 *
 * Delete a section from a profile tree.
 */
500
static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
501 502 503
{
    while (*section)
    {
504
        if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
        {
            PROFILESECTION *to_del = *section;
            *section = to_del->next;
            to_del->next = NULL;
            PROFILE_Free( to_del );
            return TRUE;
        }
        section = &(*section)->next;
    }
    return FALSE;
}


/***********************************************************************
 *           PROFILE_DeleteKey
 *
 * Delete a key from a profile tree.
 */
523
static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
524
			       LPCWSTR section_name, LPCWSTR key_name )
Alexandre Julliard's avatar
Alexandre Julliard committed
525 526 527
{
    while (*section)
    {
528
        if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
529 530 531 532
        {
            PROFILEKEY **key = &(*section)->key;
            while (*key)
            {
533
                if (!strcmpiW( (*key)->name, key_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
534 535 536
                {
                    PROFILEKEY *to_del = *key;
                    *key = to_del->next;
537
                    HeapFree( GetProcessHeap(), 0, to_del->value);
538
                    HeapFree( GetProcessHeap(), 0, to_del );
Alexandre Julliard's avatar
Alexandre Julliard committed
539 540 541 542 543 544 545 546 547 548 549
                    return TRUE;
                }
                key = &(*key)->next;
            }
        }
        section = &(*section)->next;
    }
    return FALSE;
}


550 551 552 553 554
/***********************************************************************
 *           PROFILE_DeleteAllKeys
 *
 * Delete all keys from a profile tree.
 */
555
static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
556 557 558 559
{
    PROFILESECTION **section= &CurProfile->section;
    while (*section)
    {
560
        if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
561 562 563 564 565 566
        {
            PROFILEKEY **key = &(*section)->key;
            while (*key)
            {
                PROFILEKEY *to_del = *key;
		*key = to_del->next;
567
                HeapFree( GetProcessHeap(), 0, to_del->value);
568 569 570 571 572 573 574 575 576
		HeapFree( GetProcessHeap(), 0, to_del );
		CurProfile->changed =TRUE;
            }
        }
        section = &(*section)->next;
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
577 578 579 580 581
/***********************************************************************
 *           PROFILE_Find
 *
 * Find a key in a profile tree, optionally creating it.
 */
582 583
static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
                                 LPCWSTR key_name, BOOL create, BOOL create_always )
Alexandre Julliard's avatar
Alexandre Julliard committed
584
{
585
    LPCWSTR p;
586 587
    int seclen, keylen;

588
    while (PROFILE_isspaceW(*section_name)) section_name++;
589 590 591 592 593
    if (*section_name)
        p = section_name + strlenW(section_name) - 1;
    else
        p = section_name;

594
    while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
595
    seclen = p - section_name + 1;
596

597
    while (PROFILE_isspaceW(*key_name)) key_name++;
598 599 600 601 602
    if (*key_name)
        p = key_name + strlenW(key_name) - 1;
    else
        p = key_name;

603
    while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
604 605
    keylen = p - key_name + 1;

Alexandre Julliard's avatar
Alexandre Julliard committed
606 607
    while (*section)
    {
608
        if ( ((*section)->name[0])
609
             && (!(strncmpiW( (*section)->name, section_name, seclen )))
610
             && (((*section)->name)[seclen] == '\0') )
Alexandre Julliard's avatar
Alexandre Julliard committed
611 612
        {
            PROFILEKEY **key = &(*section)->key;
613

Alexandre Julliard's avatar
Alexandre Julliard committed
614 615
            while (*key)
            {
Francois Gouget's avatar
Francois Gouget committed
616 617 618 619
                /* If create_always is FALSE then we check if the keyname
                 * already exists. Otherwise we add it regardless of its
                 * existence, to allow keys to be added more than once in
                 * some cases.
620 621 622
                 */
                if(!create_always)
                {
623
                    if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
624 625 626
                         && (((*key)->name)[keylen] == '\0') )
                        return *key;
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
627 628 629
                key = &(*key)->next;
            }
            if (!create) return NULL;
630
            if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
631
                return NULL;
632
            strcpyW( (*key)->name, key_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
633 634 635 636 637 638 639
            (*key)->value = NULL;
            (*key)->next  = NULL;
            return *key;
        }
        section = &(*section)->next;
    }
    if (!create) return NULL;
640
    *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
641
    if(*section == NULL) return NULL;
642
    strcpyW( (*section)->name, section_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
643
    (*section)->next = NULL;
644
    if (!((*section)->key  = HeapAlloc( GetProcessHeap(), 0,
645
                                        sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
646
    {
647 648
        HeapFree(GetProcessHeap(), 0, *section);
        return NULL;
649
    }
650
    strcpyW( (*section)->key->name, key_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
651 652 653 654 655 656 657 658 659 660 661
    (*section)->key->value = NULL;
    (*section)->key->next  = NULL;
    return (*section)->key;
}


/***********************************************************************
 *           PROFILE_FlushFile
 *
 * Flush the current profile to disk if changed.
 */
662
static BOOL PROFILE_FlushFile(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
663
{
664 665
    HANDLE hFile = NULL;
    FILETIME LastWriteTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
666

Alexandre Julliard's avatar
Alexandre Julliard committed
667 668
    if(!CurProfile)
    {
669
        WARN("No current profile!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
670 671 672
        return FALSE;
    }

673 674
    if (!CurProfile->changed) return TRUE;

675 676
    hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
677

678
    if (hFile == INVALID_HANDLE_VALUE)
Alexandre Julliard's avatar
Alexandre Julliard committed
679
    {
680
        WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile->filename), GetLastError());
Alexandre Julliard's avatar
Alexandre Julliard committed
681 682 683
        return FALSE;
    }

684 685 686 687 688
    TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
    PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
    if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
       CurProfile->LastWriteTime=LastWriteTime;
    CloseHandle( hFile );
Alexandre Julliard's avatar
Alexandre Julliard committed
689
    CurProfile->changed = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
690 691 692 693
    return TRUE;
}


694 695 696 697 698 699 700 701 702
/***********************************************************************
 *           PROFILE_ReleaseFile
 *
 * Flush the current profile to disk and remove it from the cache.
 */
static void PROFILE_ReleaseFile(void)
{
    PROFILE_FlushFile();
    PROFILE_Free( CurProfile->section );
703
    HeapFree( GetProcessHeap(), 0, CurProfile->filename );
704 705
    CurProfile->changed = FALSE;
    CurProfile->section = NULL;
706
    CurProfile->filename  = NULL;
707 708
    CurProfile->encoding = ENCODING_ANSI;
    ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
709 710
}

711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
/***********************************************************************
 *
 * Compares a file time with the current time. If the file time is
 * at least 2.1 seconds in the past, return true.
 *
 * Intended as cache safety measure: The time resolution on FAT is
 * two seconds, so files that are not at least two seconds old might
 * keep their time even on modification, so don't cache them.
 */
static BOOL is_not_current(FILETIME * ft)
{
    FILETIME Now;
    LONGLONG ftll, nowll;
    GetSystemTimeAsFileTime(&Now);
    ftll = ((LONGLONG)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
    nowll = ((LONGLONG)Now.dwHighDateTime << 32) + Now.dwLowDateTime;
    TRACE("%08x;%08x\n",(unsigned)ftll+21000000,(unsigned)nowll);
    return ftll + 21000000 < nowll;
}
730

Alexandre Julliard's avatar
Alexandre Julliard committed
731 732 733 734 735
/***********************************************************************
 *           PROFILE_Open
 *
 * Open a profile file, checking the cached file first.
 */
736
static BOOL PROFILE_Open( LPCWSTR filename, BOOL write_access )
Alexandre Julliard's avatar
Alexandre Julliard committed
737
{
738 739 740 741
    WCHAR windirW[MAX_PATH];
    WCHAR buffer[MAX_PATH];
    HANDLE hFile = INVALID_HANDLE_VALUE;
    FILETIME LastWriteTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
742 743
    int i,j;
    PROFILE *tempProfile;
744 745
    
    ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
Alexandre Julliard's avatar
Alexandre Julliard committed
746 747 748 749 750

    /* First time around */

    if(!CurProfile)
       for(i=0;i<N_CACHED_PROFILES;i++)
751
       {
752
          MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
753
          if(MRUProfile[i] == NULL) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
754 755 756
          MRUProfile[i]->changed=FALSE;
          MRUProfile[i]->section=NULL;
          MRUProfile[i]->filename=NULL;
757 758 759
          MRUProfile[i]->encoding=ENCODING_ANSI;
          ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
       }
Alexandre Julliard's avatar
Alexandre Julliard committed
760

761
    GetWindowsDirectoryW( windirW, MAX_PATH );
Alexandre Julliard's avatar
Alexandre Julliard committed
762

763 764 765
    if (!filename)
	filename = wininiW;

766 767
    if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
        !strchrW(filename, '\\') && !strchrW(filename, '/'))
Alexandre Julliard's avatar
Alexandre Julliard committed
768
    {
769 770 771 772
        static const WCHAR wszSeparator[] = {'\\', 0};
        strcpyW(buffer, windirW);
        strcatW(buffer, wszSeparator);
        strcatW(buffer, filename);
Alexandre Julliard's avatar
Alexandre Julliard committed
773 774 775
    }
    else
    {
776 777 778 779 780
        LPWSTR dummy;
        GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
    }
        
    TRACE("path: %s\n", debugstr_w(buffer));
781

782
    hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
783
                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
784
                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
785

786 787
    if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
    {
788
        WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
789
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
790 791
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
792
    for(i=0;i<N_CACHED_PROFILES;i++)
793
    {
794 795 796 797 798 799 800 801 802 803 804 805 806 807
        if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
        {
            TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
            if(i)
            {
                PROFILE_FlushFile();
                tempProfile=MRUProfile[i];
                for(j=i;j>0;j--)
                    MRUProfile[j]=MRUProfile[j-1];
                CurProfile=tempProfile;
            }

            if (hFile != INVALID_HANDLE_VALUE)
            {
808 809 810 811 812 813
                GetFileTime(hFile, NULL, NULL, &LastWriteTime);
                if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
                    is_not_current(&LastWriteTime))
                    TRACE("(%s): already opened (mru=%d)\n",
                          debugstr_w(buffer), i);
                else
814
                {
815 816
                    TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
                          debugstr_w(buffer), i);
817
                    PROFILE_Free(CurProfile->section);
818 819
                    CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
                    CurProfile->LastWriteTime = LastWriteTime;
820 821 822 823 824 825
                }
                CloseHandle(hFile);
            }
            else TRACE("(%s): already opened, not yet created (mru=%d)\n",
                       debugstr_w(buffer), i);
            return TRUE;
826 827
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
828

829 830
    /* Flush the old current profile */
    PROFILE_FlushFile();
Alexandre Julliard's avatar
Alexandre Julliard committed
831

832
    /* Make the oldest profile the current one only in order to get rid of it */
Alexandre Julliard's avatar
Alexandre Julliard committed
833 834 835 836 837 838 839
    if(i==N_CACHED_PROFILES)
      {
       tempProfile=MRUProfile[N_CACHED_PROFILES-1];
       for(i=N_CACHED_PROFILES-1;i>0;i--)
          MRUProfile[i]=MRUProfile[i-1];
       CurProfile=tempProfile;
      }
840
    if(CurProfile->filename) PROFILE_ReleaseFile();
Alexandre Julliard's avatar
Alexandre Julliard committed
841

842
    /* OK, now that CurProfile is definitely free we assign it our new file */
843 844
    CurProfile->filename  = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
    strcpyW( CurProfile->filename, buffer );
Alexandre Julliard's avatar
Alexandre Julliard committed
845

846
    if (hFile != INVALID_HANDLE_VALUE)
Alexandre Julliard's avatar
Alexandre Julliard committed
847
    {
848
        CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
849
        GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
850
        CloseHandle(hFile);
Alexandre Julliard's avatar
Alexandre Julliard committed
851 852
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
853 854
    {
        /* Does not exist yet, we will create it in PROFILE_FlushFile */
855
        WARN("profile file %s not found\n", debugstr_w(buffer) );
Alexandre Julliard's avatar
Alexandre Julliard committed
856
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
857 858 859 860 861 862 863
    return TRUE;
}


/***********************************************************************
 *           PROFILE_GetSection
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
864
 * Returns all keys of a section.
Alexandre Julliard's avatar
Alexandre Julliard committed
865
 * If return_values is TRUE, also include the corresponding values.
Alexandre Julliard's avatar
Alexandre Julliard committed
866
 */
867
static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
868
			       LPWSTR buffer, UINT len, BOOL return_values, BOOL return_noequalkeys )
Alexandre Julliard's avatar
Alexandre Julliard committed
869 870
{
    PROFILEKEY *key;
871 872 873

    if(!buffer) return 0;

874 875
    TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);

Alexandre Julliard's avatar
Alexandre Julliard committed
876 877
    while (section)
    {
878
        if (section->name[0] && !strcmpiW( section->name, section_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
879
        {
880
            UINT oldlen = len;
881
            for (key = section->key; key; key = key->next)
Alexandre Julliard's avatar
Alexandre Julliard committed
882 883
            {
                if (len <= 2) break;
884
                if (!*key->name) continue;  /* Skip empty lines */
Alexandre Julliard's avatar
Alexandre Julliard committed
885
                if (IS_ENTRY_COMMENT(key->name)) continue;  /* Skip comments */
886
                if (!return_noequalkeys && !return_values && !key->value) continue;  /* Skip lines w.o. '=' */
887
                PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
888 889
                len -= strlenW(buffer) + 1;
                buffer += strlenW(buffer) + 1;
890 891
		if (len < 2)
		    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
892 893
		if (return_values && key->value) {
			buffer[-1] = '=';
894
			PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
895 896
			len -= strlenW(buffer) + 1;
			buffer += strlenW(buffer) + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
897
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
898 899
            }
            *buffer = '\0';
900
            if (len <= 1)
Alexandre Julliard's avatar
Alexandre Julliard committed
901
                /*If either lpszSection or lpszKey is NULL and the supplied
902
                  destination buffer is too small to hold all the strings,
Alexandre Julliard's avatar
Alexandre Julliard committed
903 904 905 906 907 908 909
                  the last string is truncated and followed by two null characters.
                  In this case, the return value is equal to cchReturnBuffer
                  minus two. */
            {
		buffer[-1] = '\0';
                return oldlen - 2;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
910
            return oldlen - len;
Alexandre Julliard's avatar
Alexandre Julliard committed
911 912 913 914
        }
        section = section->next;
    }
    buffer[0] = buffer[1] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
915
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
916 917
}

918
/* See GetPrivateProfileSectionNamesA for documentation */
919
static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
920
{
921
    LPWSTR buf;
922
    UINT buflen,tmplen;
923 924
    PROFILESECTION *section;

925 926
    TRACE("(%p, %d)\n", buffer, len);

927 928 929 930 931 932
    if (!buffer || !len)
        return 0;
    if (len==1) {
        *buffer='\0';
        return 0;
    }
933

934
    buflen=len-1;
935 936 937 938
    buf=buffer;
    section = CurProfile->section;
    while ((section!=NULL)) {
        if (section->name[0]) {
939
            tmplen = strlenW(section->name)+1;
940
            if (tmplen >= buflen) {
941 942 943
                if (buflen > 0) {
                    memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
                    buf += buflen-1;
944 945 946 947 948
                    *buf++='\0';
                }
                *buf='\0';
                return len-2;
            }
949 950 951
            memcpy(buf, section->name, tmplen * sizeof(WCHAR));
            buf += tmplen;
            buflen -= tmplen;
952 953 954 955
        }
        section = section->next;
    }
    *buf='\0';
956 957 958 959
    return buf-buffer;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
960 961 962 963
/***********************************************************************
 *           PROFILE_GetString
 *
 * Get a profile string.
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
 *
 * Tests with GetPrivateProfileString16, W95a,
 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
 * section	key_name	def_val		res	buffer
 * "set1"	"1"		"x"		43	[data]
 * "set1"	"1   "		"x"		43	[data]		(!)
 * "set1"	"  1  "'	"x"		43	[data]		(!)
 * "set1"	""		"x"		1	"x"
 * "set1"	""		"x   "		1	"x"		(!)
 * "set1"	""		"  x   "	3	"  x"		(!)
 * "set1"	NULL		"x"		6	"1\02\03\0\0"
 * "set1"	""		"x"		1	"x"
 * NULL		"1"		"x"		0	""		(!)
 * ""		"1"		"x"		1	"x"
 * NULL		NULL		""		0	""
979 980
 *
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
981
 */
982
static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
983
                              LPCWSTR def_val, LPWSTR buffer, UINT len, BOOL win32 )
Alexandre Julliard's avatar
Alexandre Julliard committed
984 985
{
    PROFILEKEY *key = NULL;
986
    static const WCHAR empty_strW[] = { 0 };
Alexandre Julliard's avatar
Alexandre Julliard committed
987

988
    if(!buffer || !len) return 0;
989

990
    if (!def_val) def_val = empty_strW;
991
    if (key_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
992
    {
993
	if (!key_name[0])
994
        {
995 996
            PROFILE_CopyEntry(buffer, def_val, len, TRUE);
            return strlenW(buffer);
997
        }
998
        key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
999
        PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
1000
                           len, TRUE );
1001 1002 1003 1004
        TRACE("(%s,%s,%s): returning %s\n",
              debugstr_w(section), debugstr_w(key_name),
              debugstr_w(def_val), debugstr_w(buffer) );
        return strlenW( buffer );
Alexandre Julliard's avatar
Alexandre Julliard committed
1005
    }
1006
    /* no "else" here ! */
1007
    if (section && section[0])
1008
    {
1009
        INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, !win32);
1010 1011
        if (!buffer[0]) /* no luck -> def_val */
        {
1012
            PROFILE_CopyEntry(buffer, def_val, len, TRUE);
1013
            ret = strlenW(buffer);
1014 1015
        }
        return ret;
1016
    }
1017 1018
    buffer[0] = '\0';
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1019 1020 1021 1022 1023 1024 1025 1026
}


/***********************************************************************
 *           PROFILE_SetString
 *
 * Set a profile string.
 */
1027 1028
static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
                               LPCWSTR value, BOOL create_always )
Alexandre Julliard's avatar
Alexandre Julliard committed
1029 1030 1031
{
    if (!key_name)  /* Delete a whole section */
    {
1032
        TRACE("(%s)\n", debugstr_w(section_name));
Alexandre Julliard's avatar
Alexandre Julliard committed
1033 1034
        CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
                                                      section_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
1035 1036
        return TRUE;         /* Even if PROFILE_DeleteSection() has failed,
                                this is not an error on application's level.*/
Alexandre Julliard's avatar
Alexandre Julliard committed
1037 1038 1039
    }
    else if (!value)  /* Delete a key */
    {
1040
        TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1041 1042
        CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
                                                  section_name, key_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
1043
        return TRUE;          /* same error handling as above */
Alexandre Julliard's avatar
Alexandre Julliard committed
1044 1045 1046
    }
    else  /* Set the key value */
    {
1047 1048
        PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
                                        key_name, TRUE, create_always );
1049 1050
        TRACE("(%s,%s,%s):\n",
              debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1051
        if (!key) return FALSE;
1052 1053 1054 1055 1056

        /* strip the leading spaces. We can safely strip \n\r and
         * friends too, they should not happen here anyway. */
        while (PROFILE_isspaceW(*value)) value++;

Alexandre Julliard's avatar
Alexandre Julliard committed
1057 1058
        if (key->value)
        {
1059
            if (!strcmpW( key->value, value ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1060
            {
1061
                TRACE("  no change needed\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1062 1063
                return TRUE;  /* No change needed */
            }
1064
            TRACE("  replacing %s\n", debugstr_w(key->value) );
1065
            HeapFree( GetProcessHeap(), 0, key->value );
Alexandre Julliard's avatar
Alexandre Julliard committed
1066
        }
1067
        else TRACE("  creating key\n" );
1068 1069
        key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
        strcpyW( key->value, value );
Alexandre Julliard's avatar
Alexandre Julliard committed
1070
        CurProfile->changed = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1071
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1072
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1073 1074 1075
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1076 1077
/********************* API functions **********************************/

Alexandre Julliard's avatar
Alexandre Julliard committed
1078 1079

/***********************************************************************
1080
 *           GetProfileIntA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1081
 */
1082
UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
Alexandre Julliard's avatar
Alexandre Julliard committed
1083
{
1084
    return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1085 1086 1087
}

/***********************************************************************
1088
 *           GetProfileIntW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1089
 */
1090
UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
Alexandre Julliard's avatar
Alexandre Julliard committed
1091
{
1092
    return GetPrivateProfileIntW( section, entry, def_val, wininiW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1093 1094
}

1095
/*
1096 1097 1098
 * if win32, copy:
 *   - Section names if 'section' is NULL
 *   - Keys in a Section if 'entry' is NULL
1099
 * (see MSDN doc for GetPrivateProfileString)
1100
 */
1101 1102 1103
static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
					    LPCWSTR def_val, LPWSTR buffer,
					    UINT len, LPCWSTR filename,
1104
					    BOOL win32 )
1105 1106
{
    int		ret;
1107
    LPWSTR	defval_tmp = NULL;
1108

1109 1110
    TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
          debugstr_w(def_val), buffer, len, debugstr_w(filename));
1111 1112 1113 1114

    /* strip any trailing ' ' of def_val. */
    if (def_val)
    {
1115
        LPCWSTR p = def_val + strlenW(def_val) - 1;
1116 1117 1118 1119 1120 1121 1122

        while (p > def_val && *p == ' ')
            p--;

        if (p >= def_val)
        {
            int len = (int)(p - def_val) + 1;
1123

1124 1125 1126 1127 1128
            defval_tmp = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
            memcpy(defval_tmp, def_val, len * sizeof(WCHAR));
            defval_tmp[len] = '\0';
            def_val = defval_tmp;
        }
1129 1130
    }

1131
    RtlEnterCriticalSection( &PROFILE_CritSect );
1132

1133
    if (PROFILE_Open( filename, FALSE )) {
1134
	if (win32 && (section == NULL))
1135
            ret = PROFILE_GetSectionNames(buffer, len);
1136 1137
	else 
	    /* PROFILE_GetString can handle the 'entry == NULL' case */
1138 1139 1140
            ret = PROFILE_GetString( section, entry, def_val, buffer, len, win32 );
    } else if (buffer && def_val) {
       lstrcpynW( buffer, def_val, len );
1141
       ret = strlenW( buffer );
1142
    }
1143 1144
    else
       ret = 0;
1145

1146
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1147

1148
    HeapFree(GetProcessHeap(), 0, defval_tmp);
1149

1150 1151
    TRACE("returning %s, %d\n", debugstr_w(buffer), ret);

1152 1153 1154 1155
    return ret;
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1156
 *           GetPrivateProfileString   (KERNEL.128)
1157 1158 1159 1160 1161
 */
INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
                                        LPCSTR def_val, LPSTR buffer,
                                        UINT16 len, LPCSTR filename )
{
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
    UNICODE_STRING sectionW, entryW, def_valW, filenameW;
    LPWSTR bufferW;
    INT16 retW, ret = 0;

    bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
    else entryW.Buffer = NULL;
    if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
    else def_valW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
                                     def_valW.Buffer, bufferW, len,
                                     filenameW.Buffer, FALSE );
    if (len)
    {
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
        if (!ret)
        {
            ret = len - 1;
            buffer[ret] = 0;
        }
        else
            ret--; /* strip terminating 0 */
    }

    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&entryW);
    RtlFreeUnicodeString(&def_valW);
    RtlFreeUnicodeString(&filenameW);
1195
    HeapFree(GetProcessHeap(), 0, bufferW);
1196
    return ret;
1197 1198 1199
}

/***********************************************************************
1200
 *           GetPrivateProfileStringA   (KERNEL32.@)
1201 1202 1203 1204 1205
 */
INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
				     LPCSTR def_val, LPSTR buffer,
				     UINT len, LPCSTR filename )
{
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
    UNICODE_STRING sectionW, entryW, def_valW, filenameW;
    LPWSTR bufferW;
    INT retW, ret = 0;

    bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
    else entryW.Buffer = NULL;
    if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
    else def_valW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
                                     def_valW.Buffer, bufferW, len,
                                     filenameW.Buffer);
    if (len)
    {
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
        if (!ret)
        {
            ret = len - 1;
            buffer[ret] = 0;
        }
        else
            ret--; /* strip terminating 0 */
    }

    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&entryW);
    RtlFreeUnicodeString(&def_valW);
    RtlFreeUnicodeString(&filenameW);
1239
    HeapFree(GetProcessHeap(), 0, bufferW);
1240
    return ret;
1241 1242 1243
}

/***********************************************************************
1244
 *           GetPrivateProfileStringW   (KERNEL32.@)
1245 1246 1247 1248 1249
 */
INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
				     LPCWSTR def_val, LPWSTR buffer,
				     UINT len, LPCWSTR filename )
{
1250 1251
    TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));

1252 1253
    return PROFILE_GetPrivateProfileString( section, entry, def_val,
                                            buffer, len, filename, TRUE );
1254 1255
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1256
/***********************************************************************
1257
 *           GetProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1258
 */
1259
INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1260
			      LPSTR buffer, UINT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
1261
{
1262 1263
    return GetPrivateProfileStringA( section, entry, def_val,
                                     buffer, len, "win.ini" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1264
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1265 1266

/***********************************************************************
1267
 *           GetProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1268
 */
1269
INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1270
			      LPCWSTR def_val, LPWSTR buffer, UINT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
1271
{
1272
    return GetPrivateProfileStringW( section, entry, def_val,
1273
				     buffer, len, wininiW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1274 1275
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1276
/***********************************************************************
1277
 *           WriteProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1278
 */
1279
BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1280
				 LPCSTR string )
Alexandre Julliard's avatar
Alexandre Julliard committed
1281
{
1282
    return WritePrivateProfileStringA( section, entry, string, "win.ini" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1283 1284 1285
}

/***********************************************************************
1286
 *           WriteProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1287
 */
1288
BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
Alexandre Julliard's avatar
Alexandre Julliard committed
1289
                                     LPCWSTR string )
Alexandre Julliard's avatar
Alexandre Julliard committed
1290
{
1291
    return WritePrivateProfileStringW( section, entry, string, wininiW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1292 1293 1294 1295
}


/***********************************************************************
1296
 *           GetPrivateProfileIntW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1297
 */
1298 1299
UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
                                   INT def_val, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1300
{
1301 1302 1303 1304 1305 1306 1307 1308
    WCHAR buffer[30];
    UNICODE_STRING bufferW;
    INT len;
    ULONG result;

    if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
                                          buffer, sizeof(buffer)/sizeof(WCHAR),
                                          filename )))
1309
        return def_val;
1310

1311 1312 1313 1314 1315
    /* FIXME: if entry can be found but it's empty, then Win16 is
     * supposed to return 0 instead of def_val ! Difficult/problematic
     * to implement (every other failure also returns zero buffer),
     * thus wait until testing framework avail for making sure nothing
     * else gets broken that way. */
1316
    if (!buffer[0]) return (UINT)def_val;
1317

1318
    RtlInitUnicodeString( &bufferW, buffer );
1319
    RtlUnicodeStringToInteger( &bufferW, 0, &result);
1320
    return result;
Alexandre Julliard's avatar
Alexandre Julliard committed
1321 1322
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1323
/***********************************************************************
1324
 *           GetPrivateProfileIntA   (KERNEL32.@)
1325 1326
 *
 * FIXME: rewrite using unicode
Alexandre Julliard's avatar
Alexandre Julliard committed
1327
 */
1328 1329
UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
				   INT def_val, LPCSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1330
{
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
    UNICODE_STRING entryW, filenameW, sectionW;
    UINT res;
    if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
    else entryW.Buffer = NULL;
    if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;
    if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
                                filenameW.Buffer);
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&filenameW);
    RtlFreeUnicodeString(&entryW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1344 1345 1346
    return res;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1347
/***********************************************************************
1348
 *           GetPrivateProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1349
 */
1350 1351
INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
				      DWORD len, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1352
{
1353 1354 1355 1356 1357 1358 1359
    int ret = 0;

    if (!section || !buffer)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }
1360

1361
    TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1362

1363
    RtlEnterCriticalSection( &PROFILE_CritSect );
1364

1365
    if (PROFILE_Open( filename, FALSE ))
1366
        ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE, FALSE);
1367

1368
    RtlLeaveCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1369

1370
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1371
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1372

Alexandre Julliard's avatar
Alexandre Julliard committed
1373
/***********************************************************************
1374
 *           GetPrivateProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1375
 */
1376 1377 1378 1379 1380 1381
INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
                                      DWORD len, LPCSTR filename )
{
    UNICODE_STRING sectionW, filenameW;
    LPWSTR bufferW;
    INT retW, ret = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1382

1383 1384 1385 1386 1387 1388 1389 1390
    if (!section || !buffer)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
    RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1391 1392
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1393

1394 1395 1396
    retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
    if (len > 2)
    {
1397
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1398
        if (ret > 2)
1399
            ret -= 1;
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
        else
        {
            ret = 0;
            buffer[len-2] = 0;
            buffer[len-1] = 0;
        }
    }
    else
    {
        buffer[0] = 0;
        buffer[1] = 0;
    }

    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&filenameW);
1415
    HeapFree(GetProcessHeap(), 0, bufferW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1416 1417 1418
    return ret;
}

1419
/***********************************************************************
1420
 *           GetProfileSectionA   (KERNEL32.@)
1421 1422 1423 1424 1425 1426 1427
 */
INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
{
    return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
}

/***********************************************************************
1428
 *           GetProfileSectionW   (KERNEL32.@)
1429 1430 1431 1432 1433 1434 1435
 */
INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
{
    return GetPrivateProfileSectionW( section, buffer, len, wininiW );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1436
/***********************************************************************
1437
 *           WritePrivateProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1438
 */
1439 1440
BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
					LPCWSTR string, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1441
{
1442
    BOOL ret = FALSE;
1443

1444
    RtlEnterCriticalSection( &PROFILE_CritSect );
1445

1446
    if (!section && !entry && !string) /* documented "file flush" case */
1447
    {
1448
        if (!filename || PROFILE_Open( filename, TRUE ))
1449
        {
1450 1451 1452
            if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
        }
    }
1453
    else if (PROFILE_Open( filename, TRUE ))
1454 1455
    {
        if (!section) {
1456
            SetLastError(ERROR_FILE_NOT_FOUND);
1457 1458
        } else {
            ret = PROFILE_SetString( section, entry, string, FALSE);
1459 1460
            PROFILE_FlushFile();
        }
1461 1462
    }

1463
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1464
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1465 1466
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1467
/***********************************************************************
1468
 *           WritePrivateProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1469
 */
1470 1471
BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
					LPCSTR string, LPCSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1472
{
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
    UNICODE_STRING sectionW, entryW, stringW, filenameW;
    BOOL ret;

    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
    else entryW.Buffer = NULL;
    if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
    else stringW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
                                     stringW.Buffer, filenameW.Buffer);
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&entryW);
    RtlFreeUnicodeString(&stringW);
    RtlFreeUnicodeString(&filenameW);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1492 1493
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1494
/***********************************************************************
1495
 *           WritePrivateProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1496
 */
1497 1498
BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
                                         LPCWSTR string, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1499
{
1500
    BOOL ret = FALSE;
1501
    LPWSTR p;
1502

1503
    RtlEnterCriticalSection( &PROFILE_CritSect );
1504

1505 1506
    if (!section && !string)
    {
1507
        if (!filename || PROFILE_Open( filename, TRUE ))
1508 1509 1510 1511
        {
            if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
        }
    }
1512
    else if (PROFILE_Open( filename, TRUE )) {
1513
        if (!string) {/* delete the named section*/
1514
	    ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1515 1516
	    PROFILE_FlushFile();
        } else {
1517 1518 1519
	    PROFILE_DeleteAllKeys(section);
	    ret = TRUE;
	    while(*string) {
1520 1521 1522
                LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
                strcpyW( buf, string );
                if((p = strchrW( buf, '='))) {
1523
                    *p='\0';
1524
                    ret = PROFILE_SetString( section, buf, p+1, TRUE);
1525 1526
                }
                HeapFree( GetProcessHeap(), 0, buf );
1527
                string += strlenW(string)+1;
1528
            }
1529
            PROFILE_FlushFile();
1530
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1531
    }
1532

1533
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1534
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1535 1536
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1537
/***********************************************************************
1538
 *           WritePrivateProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1539
 */
1540 1541
BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
                                         LPCSTR string, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1542 1543

{
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
    UNICODE_STRING sectionW, filenameW;
    LPWSTR stringW;
    BOOL ret;

    if (string)
    {
        INT lenA, lenW;
        LPCSTR p = string;

        while(*p) p += strlen(p) + 1;
        lenA = p - string + 1;
        lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
        if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
            MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
    }
    else stringW = NULL;
    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);

    HeapFree(GetProcessHeap(), 0, stringW);
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&filenameW);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1571 1572 1573
}

/***********************************************************************
1574
 *           WriteProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1575
 */
1576
BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1577

Alexandre Julliard's avatar
Alexandre Julliard committed
1578
{
1579
    return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
Alexandre Julliard's avatar
Alexandre Julliard committed
1580 1581 1582
}

/***********************************************************************
1583
 *           WriteProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1584
 */
1585
BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
Alexandre Julliard's avatar
Alexandre Julliard committed
1586
{
1587
   return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1588 1589 1590 1591
}


/***********************************************************************
1592
 *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1593 1594 1595
 *
 * Returns the section names contained in the specified file.
 * FIXME: Where do we find this file when the path is relative?
1596 1597
 * The section names are returned as a list of strings with an extra
 * '\0' to mark the end of the list. Except for that the behavior
1598 1599 1600
 * depends on the Windows version.
 *
 * Win95:
1601
 * - if the buffer is 0 or 1 character long then it is as if it was of
1602
 *   infinite length.
1603
 * - otherwise, if the buffer is too small only the section names that fit
1604
 *   are returned.
1605
 * - note that this means if the buffer was too small to return even just
1606
 *   the first section name then a single '\0' will be returned.
1607
 * - the return value is the number of characters written in the buffer,
1608
 *   except if the buffer was too small in which case len-2 is returned
1609 1610
 *
 * Win2000:
1611
 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1612
 *   '\0' and the return value is 0
1613 1614
 * - otherwise if the buffer is too small then the first section name that
 *   does not fit is truncated so that the string list can be terminated
1615
 *   correctly (double '\0')
1616 1617
 * - the return value is the number of characters written in the buffer
 *   except for the trailing '\0'. If the buffer is too small, then the
1618
 *   return value is len-2
1619 1620
 * - Win2000 has a bug that triggers when the section names and the
 *   trailing '\0' fit exactly in the buffer. In that case the trailing
1621 1622 1623 1624
 *   '\0' is missing.
 *
 * Wine implements the observed Win2000 behavior (except for the bug).
 *
1625
 * Note that when the buffer is big enough then the return value may be any
1626
 * value between 1 and len-1 (or len in Win95), including len-2.
Alexandre Julliard's avatar
Alexandre Julliard committed
1627
 */
1628 1629
DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
					     LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1630
{
1631 1632
    DWORD ret = 0;

1633
    RtlEnterCriticalSection( &PROFILE_CritSect );
1634

1635
    if (PROFILE_Open( filename, FALSE ))
1636 1637
        ret = PROFILE_GetSectionNames(buffer, size);

1638
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1639 1640

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1641 1642 1643 1644
}


/***********************************************************************
1645
 *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1646
 */
1647 1648
DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
					     LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1649
{
1650 1651 1652
    UNICODE_STRING filenameW;
    LPWSTR bufferW;
    INT retW, ret = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1653

1654 1655 1656 1657 1658 1659 1660
    bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
    if (retW && size)
    {
1661
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1662 1663
        if (!ret)
        {
1664
            ret = size-2;
1665 1666
            buffer[size-1] = 0;
        }
1667 1668
        else
          ret = ret-1;
1669
    }
1670 1671
    else if(size)
        buffer[0] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
1672

1673
    RtlFreeUnicodeString(&filenameW);
1674
    HeapFree(GetProcessHeap(), 0, bufferW);
1675
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1676 1677
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1678
/***********************************************************************
1679
 *           GetPrivateProfileStructW (KERNEL32.@)
1680 1681
 *
 * Should match Win95's behaviour pretty much
Alexandre Julliard's avatar
Alexandre Julliard committed
1682
 */
1683 1684
BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
                                      LPVOID buf, UINT len, LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1685
{
1686 1687
    BOOL	ret = FALSE;

1688
    RtlEnterCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1689

1690
    if (PROFILE_Open( filename, FALSE )) {
1691
        PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1692
	if (k) {
1693 1694
	    TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
	    if (((strlenW(k->value) - 2) / 2) == len)
1695
	    {
1696
		LPWSTR end, p;
1697
		BOOL valid = TRUE;
1698
		WCHAR c;
1699 1700
		DWORD chksum = 0;

1701
	        end  = k->value + strlenW(k->value); /* -> '\0' */
1702 1703 1704
	        /* check for invalid chars in ASCII coded hex string */
	        for (p=k->value; p < end; p++)
		{
1705
                    if (!isxdigitW(*p))
1706
		    {
1707 1708
			WARN("invalid char '%x' in file %s->[%s]->%s !\n",
                             *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1709 1710 1711 1712 1713 1714 1715 1716
		        valid = FALSE;
		        break;
		    }
		}
		if (valid)
		{
		    BOOL highnibble = TRUE;
		    BYTE b = 0, val;
1717
                    LPBYTE binbuf = buf;
1718

1719 1720 1721 1722
	            end -= 2; /* don't include checksum in output data */
	            /* translate ASCII hex format into binary data */
                    for (p=k->value; p < end; p++)
            	    {
1723
	        	c = toupperW(*p);
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
			val = (c > '9') ?
				(c - 'A' + 10) : (c - '0');

			if (highnibble)
		    	    b = val << 4;
			else
			{
		    	    b += val;
		    	    *binbuf++ = b; /* feed binary data into output */
		    	    chksum += b; /* calculate checksum */
			}
			highnibble ^= 1; /* toggle */
            	    }
		    /* retrieve stored checksum value */
1738
		    c = toupperW(*p++);
1739
		    b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1740
		    c = toupperW(*p);
1741 1742 1743 1744 1745
		    b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
	            if (b == (chksum & 0xff)) /* checksums match ? */
                        ret = TRUE;
                }
            }
1746
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1747
    }
1748
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1749

1750
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1751 1752
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1753
/***********************************************************************
1754
 *           GetPrivateProfileStructA (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1755
 */
1756 1757
BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
				      LPVOID buffer, UINT len, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1758
{
1759 1760
    UNICODE_STRING sectionW, keyW, filenameW;
    INT ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1761

1762 1763 1764 1765 1766 1767 1768 1769 1770 1771
    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
    else keyW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
                                   filenameW.Buffer);
    /* Do not translate binary data. */
Alexandre Julliard's avatar
Alexandre Julliard committed
1772

1773 1774 1775
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&keyW);
    RtlFreeUnicodeString(&filenameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1776 1777 1778 1779 1780
    return ret;
}



Alexandre Julliard's avatar
Alexandre Julliard committed
1781
/***********************************************************************
1782
 *           WritePrivateProfileStructW (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1783
 */
1784 1785
BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
                                        LPVOID buf, UINT bufsize, LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1786
{
1787
    BOOL ret = FALSE;
1788
    LPBYTE binbuf;
1789
    LPWSTR outstring, p;
1790
    DWORD sum = 0;
1791 1792

    if (!section && !key && !buf)  /* flush the cache */
1793
        return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1794

1795
    /* allocate string buffer for hex chars + checksum hex char + '\0' */
1796
    outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807
    p = outstring;
    for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
      *p++ = hex[*binbuf >> 4];
      *p++ = hex[*binbuf & 0xf];
      sum += *binbuf;
    }
    /* checksum is sum & 0xff */
    *p++ = hex[(sum & 0xf0) >> 4];
    *p++ = hex[sum & 0xf];
    *p++ = '\0';

1808
    RtlEnterCriticalSection( &PROFILE_CritSect );
1809

1810
    if (PROFILE_Open( filename, TRUE )) {
1811
        ret = PROFILE_SetString( section, key, outstring, FALSE);
1812 1813
        PROFILE_FlushFile();
    }
1814

1815
    RtlLeaveCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1816

1817
    HeapFree( GetProcessHeap(), 0, outstring );
1818

1819
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1820 1821
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1822
/***********************************************************************
1823
 *           WritePrivateProfileStructA (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1824
 */
1825 1826
BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
					LPVOID buf, UINT bufsize, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1827
{
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840
    UNICODE_STRING sectionW, keyW, filenameW;
    INT ret;

    if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
    else sectionW.Buffer = NULL;
    if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
    else keyW.Buffer = NULL;
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;

    /* Do not translate binary data. */
    ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
                                     filenameW.Buffer);
Alexandre Julliard's avatar
Alexandre Julliard committed
1841

1842 1843 1844
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&keyW);
    RtlFreeUnicodeString(&filenameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1845 1846 1847
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1848

Alexandre Julliard's avatar
Alexandre Julliard committed
1849 1850 1851
/***********************************************************************
 *           WriteOutProfiles   (KERNEL.315)
 */
1852
void WINAPI WriteOutProfiles16(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1853
{
1854
    RtlEnterCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1855
    PROFILE_FlushFile();
1856
    RtlLeaveCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1857
}
1858

1859 1860 1861 1862 1863 1864 1865 1866 1867
/***********************************************************************
 *           OpenProfileUserMapping   (KERNEL32.@)
 */
BOOL WINAPI OpenProfileUserMapping(void) {
    FIXME("(), stub!\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}

1868
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1869
 *           CloseProfileUserMapping   (KERNEL32.@)
1870 1871 1872 1873 1874 1875
 */
BOOL WINAPI CloseProfileUserMapping(void) {
    FIXME("(), stub!\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}