profile.c 56.5 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/unicode.h"
34 35
#include "wine/library.h"
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
36

37
WINE_DEFAULT_DEBUG_CHANNEL(profile);
38

39 40 41 42 43 44 45 46 47 48
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
49 50
typedef struct tagPROFILEKEY
{
51
    WCHAR                 *value;
Alexandre Julliard's avatar
Alexandre Julliard committed
52
    struct tagPROFILEKEY  *next;
53
    WCHAR                  name[1];
Alexandre Julliard's avatar
Alexandre Julliard committed
54 55 56 57 58 59
} PROFILEKEY;

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


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


Alexandre Julliard's avatar
Alexandre Julliard committed
74 75 76 77 78 79
#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
80 81 82 83

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

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

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

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

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

109 110
    if(!buffer) return;

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

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

120 121 122 123
/* byte-swaps shorts in-place in a buffer. len is in WCHARs */
static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
{
    int i;
124
    USHORT * shortbuffer = buffer;
125 126 127 128 129 130 131 132
    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;
133
    WCHAR bom;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    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)
{
154 155
    char * write_buffer;
    int write_buffer_len;
156 157 158 159 160 161 162
    DWORD dwBytesWritten;

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

    switch (encoding)
    {
    case ENCODING_ANSI:
163 164 165 166 167 168
        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);
169 170
        break;
    case ENCODING_UTF8:
171 172 173 174 175 176
        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);
177 178 179 180 181 182 183 184 185 186 187 188
        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
189 190 191 192 193 194

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

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

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

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

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

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

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

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


/***********************************************************************
 *           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;
262
            HeapFree( GetProcessHeap(), 0, key->value );
263
            HeapFree( GetProcessHeap(), 0, key );
Alexandre Julliard's avatar
Alexandre Julliard committed
264 265
        }
        next_section = section->next;
266
        HeapFree( GetProcessHeap(), 0, section );
Alexandre Julliard's avatar
Alexandre Julliard committed
267 268 269
    }
}

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

277 278
static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
{
279 280 281
    int flags = IS_TEXT_UNICODE_SIGNATURE |
                IS_TEXT_UNICODE_REVERSE_SIGNATURE |
                IS_TEXT_UNICODE_ODD_LENGTH;
282 283 284 285 286
    if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
    {
        *len = sizeof(bom_utf8);
        return ENCODING_UTF8;
    }
287
    RtlIsTextUnicode(buffer, *len, &flags);
288 289 290 291 292 293 294 295 296 297 298 299 300 301
    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
302 303 304 305 306 307

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

493

Alexandre Julliard's avatar
Alexandre Julliard committed
494 495 496 497 498
/***********************************************************************
 *           PROFILE_DeleteSection
 *
 * Delete a section from a profile tree.
 */
499
static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
500 501 502
{
    while (*section)
    {
503
        if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
        {
            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.
 */
522
static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
523
			       LPCWSTR section_name, LPCWSTR key_name )
Alexandre Julliard's avatar
Alexandre Julliard committed
524 525 526
{
    while (*section)
    {
527
        if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
528 529 530 531
        {
            PROFILEKEY **key = &(*section)->key;
            while (*key)
            {
532
                if (!strcmpiW( (*key)->name, key_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
533 534 535
                {
                    PROFILEKEY *to_del = *key;
                    *key = to_del->next;
536
                    HeapFree( GetProcessHeap(), 0, to_del->value);
537
                    HeapFree( GetProcessHeap(), 0, to_del );
Alexandre Julliard's avatar
Alexandre Julliard committed
538 539 540 541 542 543 544 545 546 547 548
                    return TRUE;
                }
                key = &(*key)->next;
            }
        }
        section = &(*section)->next;
    }
    return FALSE;
}


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


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

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

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
613 614
            while (*key)
            {
Francois Gouget's avatar
Francois Gouget committed
615 616 617 618
                /* 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.
619 620 621
                 */
                if(!create_always)
                {
622
                    if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
623 624 625
                         && (((*key)->name)[keylen] == '\0') )
                        return *key;
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
626 627 628
                key = &(*key)->next;
            }
            if (!create) return NULL;
629
            if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
630
                return NULL;
631
            strcpyW( (*key)->name, key_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
632 633 634 635 636 637 638
            (*key)->value = NULL;
            (*key)->next  = NULL;
            return *key;
        }
        section = &(*section)->next;
    }
    if (!create) return NULL;
639
    *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
640
    if(*section == NULL) return NULL;
641
    strcpyW( (*section)->name, section_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
642
    (*section)->next = NULL;
643
    if (!((*section)->key  = HeapAlloc( GetProcessHeap(), 0,
644
                                        sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
645
    {
646 647
        HeapFree(GetProcessHeap(), 0, *section);
        return NULL;
648
    }
649
    strcpyW( (*section)->key->name, key_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
650 651 652 653 654 655 656 657 658 659 660
    (*section)->key->value = NULL;
    (*section)->key->next  = NULL;
    return (*section)->key;
}


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

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

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

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

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

683 684 685 686 687
    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
688
    CurProfile->changed = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
689 690 691 692
    return TRUE;
}


693 694 695 696 697 698 699 700 701
/***********************************************************************
 *           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 );
702
    HeapFree( GetProcessHeap(), 0, CurProfile->filename );
703 704
    CurProfile->changed = FALSE;
    CurProfile->section = NULL;
705
    CurProfile->filename  = NULL;
706 707
    CurProfile->encoding = ENCODING_ANSI;
    ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
708 709
}

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
/***********************************************************************
 *
 * 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;
}
729

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

    /* First time around */

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

759 760 761
    if (!filename)
	filename = wininiW;

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
790
    for(i=0;i<N_CACHED_PROFILES;i++)
791
    {
792 793 794 795 796 797 798 799 800 801 802 803 804 805
        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)
            {
806 807 808 809 810 811
                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
812
                {
813 814
                    TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
                          debugstr_w(buffer), i);
815
                    PROFILE_Free(CurProfile->section);
816 817
                    CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
                    CurProfile->LastWriteTime = LastWriteTime;
818 819 820 821 822 823
                }
                CloseHandle(hFile);
            }
            else TRACE("(%s): already opened, not yet created (mru=%d)\n",
                       debugstr_w(buffer), i);
            return TRUE;
824 825
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
826

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

830
    /* Make the oldest profile the current one only in order to get rid of it */
Alexandre Julliard's avatar
Alexandre Julliard committed
831 832 833 834 835 836 837
    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;
      }
838
    if(CurProfile->filename) PROFILE_ReleaseFile();
Alexandre Julliard's avatar
Alexandre Julliard committed
839

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

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


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

    if(!buffer) return 0;

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

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

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

923 924
    TRACE("(%p, %d)\n", buffer, len);

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

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


Alexandre Julliard's avatar
Alexandre Julliard committed
958 959 960 961
/***********************************************************************
 *           PROFILE_GetString
 *
 * Get a profile string.
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
 *
 * 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	""
977 978
 *
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
979
 */
980
static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
981
                              LPCWSTR def_val, LPWSTR buffer, UINT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
982 983
{
    PROFILEKEY *key = NULL;
984
    static const WCHAR empty_strW[] = { 0 };
Alexandre Julliard's avatar
Alexandre Julliard committed
985

986
    if(!buffer || !len) return 0;
987

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


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

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


Alexandre Julliard's avatar
Alexandre Julliard committed
1074 1075
/********************* API functions **********************************/

Alexandre Julliard's avatar
Alexandre Julliard committed
1076 1077

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

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

1093 1094
/***********************************************************************
 *           GetPrivateProfileStringW   (KERNEL32.@)
1095
 */
1096 1097 1098
INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
				     LPCWSTR def_val, LPWSTR buffer,
				     UINT len, LPCWSTR filename )
1099 1100
{
    int		ret;
1101
    LPWSTR	defval_tmp = NULL;
1102

1103 1104
    TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
          debugstr_w(def_val), buffer, len, debugstr_w(filename));
1105 1106 1107 1108

    /* strip any trailing ' ' of def_val. */
    if (def_val)
    {
1109
        LPCWSTR p = def_val + strlenW(def_val) - 1;
1110 1111 1112 1113 1114 1115

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

        if (p >= def_val)
        {
1116
            int vlen = (int)(p - def_val) + 1;
1117

1118 1119 1120
            defval_tmp = HeapAlloc(GetProcessHeap(), 0, (vlen + 1) * sizeof(WCHAR));
            memcpy(defval_tmp, def_val, vlen * sizeof(WCHAR));
            defval_tmp[vlen] = '\0';
1121 1122
            def_val = defval_tmp;
        }
1123 1124
    }

1125
    RtlEnterCriticalSection( &PROFILE_CritSect );
1126

1127
    if (PROFILE_Open( filename, FALSE )) {
1128
	if (section == NULL)
1129
            ret = PROFILE_GetSectionNames(buffer, len);
1130 1131
	else 
	    /* PROFILE_GetString can handle the 'entry == NULL' case */
1132
            ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1133 1134
    } else if (buffer && def_val) {
       lstrcpynW( buffer, def_val, len );
1135
       ret = strlenW( buffer );
1136
    }
1137 1138
    else
       ret = 0;
1139

1140
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1141

1142
    HeapFree(GetProcessHeap(), 0, defval_tmp);
1143

1144 1145
    TRACE("returning %s, %d\n", debugstr_w(buffer), ret);

1146 1147 1148 1149
    return ret;
}

/***********************************************************************
1150
 *           GetPrivateProfileStringA   (KERNEL32.@)
1151 1152 1153 1154 1155
 */
INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
				     LPCSTR def_val, LPSTR buffer,
				     UINT len, LPCSTR filename )
{
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    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);
1173
    if (len && buffer)
1174
    {
1175
        if (retW)
1176
        {
1177 1178 1179
            ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, len - 1, NULL, NULL);
            if (!ret)
                ret = len - 1;
1180
        }
1181
        buffer[ret] = 0;
1182 1183 1184 1185 1186 1187
    }

    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&entryW);
    RtlFreeUnicodeString(&def_valW);
    RtlFreeUnicodeString(&filenameW);
1188
    HeapFree(GetProcessHeap(), 0, bufferW);
1189
    return ret;
1190 1191
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1192
/***********************************************************************
1193
 *           GetProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1194
 */
1195
INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1196
			      LPSTR buffer, UINT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
1197
{
1198 1199
    return GetPrivateProfileStringA( section, entry, def_val,
                                     buffer, len, "win.ini" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1200
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1201 1202

/***********************************************************************
1203
 *           GetProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1204
 */
1205
INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1206
			      LPCWSTR def_val, LPWSTR buffer, UINT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
1207
{
1208
    return GetPrivateProfileStringW( section, entry, def_val,
1209
				     buffer, len, wininiW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1210 1211
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1212
/***********************************************************************
1213
 *           WriteProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1214
 */
1215
BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1216
				 LPCSTR string )
Alexandre Julliard's avatar
Alexandre Julliard committed
1217
{
1218
    return WritePrivateProfileStringA( section, entry, string, "win.ini" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1219 1220 1221
}

/***********************************************************************
1222
 *           WriteProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1223
 */
1224
BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
Alexandre Julliard's avatar
Alexandre Julliard committed
1225
                                     LPCWSTR string )
Alexandre Julliard's avatar
Alexandre Julliard committed
1226
{
1227
    return WritePrivateProfileStringW( section, entry, string, wininiW );
Alexandre Julliard's avatar
Alexandre Julliard committed
1228 1229 1230 1231
}


/***********************************************************************
1232
 *           GetPrivateProfileIntW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1233
 */
1234 1235
UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
                                   INT def_val, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1236
{
1237 1238 1239 1240 1241 1242 1243 1244
    WCHAR buffer[30];
    UNICODE_STRING bufferW;
    INT len;
    ULONG result;

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

1247 1248 1249 1250 1251
    /* 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. */
1252
    if (!buffer[0]) return (UINT)def_val;
1253

1254
    RtlInitUnicodeString( &bufferW, buffer );
1255
    RtlUnicodeStringToInteger( &bufferW, 0, &result);
1256
    return result;
Alexandre Julliard's avatar
Alexandre Julliard committed
1257 1258
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1259
/***********************************************************************
1260
 *           GetPrivateProfileIntA   (KERNEL32.@)
1261 1262
 *
 * FIXME: rewrite using unicode
Alexandre Julliard's avatar
Alexandre Julliard committed
1263
 */
1264 1265
UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
				   INT def_val, LPCSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1266
{
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
    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
1280 1281 1282
    return res;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1283
/***********************************************************************
1284
 *           GetPrivateProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1285
 */
1286 1287
INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
				      DWORD len, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1288
{
1289 1290 1291 1292 1293 1294 1295
    int ret = 0;

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

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

1299
    RtlEnterCriticalSection( &PROFILE_CritSect );
1300

1301
    if (PROFILE_Open( filename, FALSE ))
1302
        ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1303

1304
    RtlLeaveCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1305

1306
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1307
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1308

Alexandre Julliard's avatar
Alexandre Julliard committed
1309
/***********************************************************************
1310
 *           GetPrivateProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1311
 */
1312 1313 1314 1315 1316 1317
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
1318

1319 1320 1321 1322 1323 1324
    if (!section || !buffer)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

1325
    bufferW = HeapAlloc(GetProcessHeap(), 0, len * 2 * sizeof(WCHAR));
1326
    RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1327 1328
    if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
    else filenameW.Buffer = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1329

1330 1331
    retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len * 2, filenameW.Buffer);
    if (retW)
1332
    {
1333
        if (retW == len * 2 - 2) retW++;  /* overflow */
1334
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1335
        if (!ret || ret == len)  /* overflow */
1336
        {
1337
            ret = len - 2;
1338 1339 1340
            buffer[len-2] = 0;
            buffer[len-1] = 0;
        }
1341
        else ret--;
1342 1343 1344 1345 1346 1347 1348 1349 1350
    }
    else
    {
        buffer[0] = 0;
        buffer[1] = 0;
    }

    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&filenameW);
1351
    HeapFree(GetProcessHeap(), 0, bufferW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1352 1353 1354
    return ret;
}

1355
/***********************************************************************
1356
 *           GetProfileSectionA   (KERNEL32.@)
1357 1358 1359 1360 1361 1362 1363
 */
INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
{
    return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
}

/***********************************************************************
1364
 *           GetProfileSectionW   (KERNEL32.@)
1365 1366 1367 1368 1369 1370 1371
 */
INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
{
    return GetPrivateProfileSectionW( section, buffer, len, wininiW );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1372
/***********************************************************************
1373
 *           WritePrivateProfileStringW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1374
 */
1375 1376
BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
					LPCWSTR string, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1377
{
1378
    BOOL ret = FALSE;
1379

1380
    RtlEnterCriticalSection( &PROFILE_CritSect );
1381

1382
    if (!section && !entry && !string) /* documented "file flush" case */
1383
    {
1384
        if (!filename || PROFILE_Open( filename, TRUE ))
1385
        {
1386 1387 1388
            if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
        }
    }
1389
    else if (PROFILE_Open( filename, TRUE ))
1390 1391
    {
        if (!section) {
1392
            SetLastError(ERROR_FILE_NOT_FOUND);
1393 1394
        } else {
            ret = PROFILE_SetString( section, entry, string, FALSE);
1395 1396
            PROFILE_FlushFile();
        }
1397 1398
    }

1399
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1400
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1401 1402
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1403
/***********************************************************************
1404
 *           WritePrivateProfileStringA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1405
 */
1406 1407
BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
					LPCSTR string, LPCSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1408
{
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
    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
1428 1429
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1430
/***********************************************************************
1431
 *           WritePrivateProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1432
 */
1433 1434
BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
                                         LPCWSTR string, LPCWSTR filename )
Alexandre Julliard's avatar
Alexandre Julliard committed
1435
{
1436
    BOOL ret = FALSE;
1437
    LPWSTR p;
1438

1439
    RtlEnterCriticalSection( &PROFILE_CritSect );
1440

1441 1442
    if (!section && !string)
    {
1443
        if (!filename || PROFILE_Open( filename, TRUE ))
1444 1445 1446 1447
        {
            if (CurProfile) PROFILE_ReleaseFile();  /* always return FALSE in this case */
        }
    }
1448
    else if (PROFILE_Open( filename, TRUE )) {
1449
        if (!string) {/* delete the named section*/
1450
	    ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1451 1452
	    PROFILE_FlushFile();
        } else {
1453 1454 1455
	    PROFILE_DeleteAllKeys(section);
	    ret = TRUE;
	    while(*string) {
1456 1457 1458
                LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
                strcpyW( buf, string );
                if((p = strchrW( buf, '='))) {
1459
                    *p='\0';
1460
                    ret = PROFILE_SetString( section, buf, p+1, TRUE);
1461 1462
                }
                HeapFree( GetProcessHeap(), 0, buf );
1463
                string += strlenW(string)+1;
1464
            }
1465
            PROFILE_FlushFile();
1466
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1467
    }
1468

1469
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1470
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1471 1472
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1473
/***********************************************************************
1474
 *           WritePrivateProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1475
 */
1476 1477
BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
                                         LPCSTR string, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1478 1479

{
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
    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
1507 1508 1509
}

/***********************************************************************
1510
 *           WriteProfileSectionA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1511
 */
1512
BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1513

Alexandre Julliard's avatar
Alexandre Julliard committed
1514
{
1515
    return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
Alexandre Julliard's avatar
Alexandre Julliard committed
1516 1517 1518
}

/***********************************************************************
1519
 *           WriteProfileSectionW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1520
 */
1521
BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
Alexandre Julliard's avatar
Alexandre Julliard committed
1522
{
1523
   return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1524 1525 1526 1527
}


/***********************************************************************
1528
 *           GetPrivateProfileSectionNamesW  (KERNEL32.@)
1529 1530 1531
 *
 * Returns the section names contained in the specified file.
 * FIXME: Where do we find this file when the path is relative?
1532 1533
 * 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
1534 1535 1536
 * depends on the Windows version.
 *
 * Win95:
1537
 * - if the buffer is 0 or 1 character long then it is as if it was of
1538
 *   infinite length.
1539
 * - otherwise, if the buffer is too small only the section names that fit
1540
 *   are returned.
1541
 * - note that this means if the buffer was too small to return even just
1542
 *   the first section name then a single '\0' will be returned.
1543
 * - the return value is the number of characters written in the buffer,
1544
 *   except if the buffer was too small in which case len-2 is returned
1545 1546
 *
 * Win2000:
1547
 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1548
 *   '\0' and the return value is 0
1549 1550
 * - 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
1551
 *   correctly (double '\0')
1552 1553
 * - 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
1554
 *   return value is len-2
1555 1556
 * - Win2000 has a bug that triggers when the section names and the
 *   trailing '\0' fit exactly in the buffer. In that case the trailing
1557 1558 1559 1560
 *   '\0' is missing.
 *
 * Wine implements the observed Win2000 behavior (except for the bug).
 *
1561
 * Note that when the buffer is big enough then the return value may be any
1562
 * value between 1 and len-1 (or len in Win95), including len-2.
Alexandre Julliard's avatar
Alexandre Julliard committed
1563
 */
1564 1565
DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
					     LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1566
{
1567 1568
    DWORD ret = 0;

1569
    RtlEnterCriticalSection( &PROFILE_CritSect );
1570

1571
    if (PROFILE_Open( filename, FALSE ))
1572 1573
        ret = PROFILE_GetSectionNames(buffer, size);

1574
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1575 1576

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1577 1578 1579 1580
}


/***********************************************************************
1581
 *           GetPrivateProfileSectionNamesA  (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1582
 */
1583 1584
DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
					     LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1585
{
1586 1587 1588
    UNICODE_STRING filenameW;
    LPWSTR bufferW;
    INT retW, ret = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1589

1590 1591 1592 1593 1594 1595 1596
    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)
    {
1597
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1598 1599
        if (!ret)
        {
1600
            ret = size-2;
1601 1602
            buffer[size-1] = 0;
        }
1603 1604
        else
          ret = ret-1;
1605
    }
1606 1607
    else if(size)
        buffer[0] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
1608

1609
    RtlFreeUnicodeString(&filenameW);
1610
    HeapFree(GetProcessHeap(), 0, bufferW);
1611
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1612 1613
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1614
/***********************************************************************
1615
 *           GetPrivateProfileStructW (KERNEL32.@)
1616 1617
 *
 * Should match Win95's behaviour pretty much
Alexandre Julliard's avatar
Alexandre Julliard committed
1618
 */
1619 1620
BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
                                      LPVOID buf, UINT len, LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1621
{
1622 1623
    BOOL	ret = FALSE;

1624
    RtlEnterCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1625

1626
    if (PROFILE_Open( filename, FALSE )) {
1627
        PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1628
	if (k) {
1629 1630
	    TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
	    if (((strlenW(k->value) - 2) / 2) == len)
1631
	    {
1632
		LPWSTR end, p;
1633
		BOOL valid = TRUE;
1634
		WCHAR c;
1635 1636
		DWORD chksum = 0;

1637
	        end  = k->value + strlenW(k->value); /* -> '\0' */
1638 1639 1640
	        /* check for invalid chars in ASCII coded hex string */
	        for (p=k->value; p < end; p++)
		{
1641
                    if (!isxdigitW(*p))
1642
		    {
1643 1644
			WARN("invalid char '%x' in file %s->[%s]->%s !\n",
                             *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1645 1646 1647 1648 1649 1650 1651 1652
		        valid = FALSE;
		        break;
		    }
		}
		if (valid)
		{
		    BOOL highnibble = TRUE;
		    BYTE b = 0, val;
1653
                    LPBYTE binbuf = buf;
1654

1655 1656 1657 1658
	            end -= 2; /* don't include checksum in output data */
	            /* translate ASCII hex format into binary data */
                    for (p=k->value; p < end; p++)
            	    {
1659
	        	c = toupperW(*p);
1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
			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 */
1674
		    c = toupperW(*p++);
1675
		    b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1676
		    c = toupperW(*p);
1677 1678 1679 1680 1681
		    b +=  (c > '9') ? (c - 'A' + 10) : (c - '0');
	            if (b == (chksum & 0xff)) /* checksums match ? */
                        ret = TRUE;
                }
            }
1682
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1683
    }
1684
    RtlLeaveCriticalSection( &PROFILE_CritSect );
1685

1686
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1687 1688
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1689
/***********************************************************************
1690
 *           GetPrivateProfileStructA (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1691
 */
1692 1693
BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
				      LPVOID buffer, UINT len, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1694
{
1695 1696
    UNICODE_STRING sectionW, keyW, filenameW;
    INT ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1697

1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
    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
1708

1709 1710 1711
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&keyW);
    RtlFreeUnicodeString(&filenameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1712 1713 1714 1715 1716
    return ret;
}



Alexandre Julliard's avatar
Alexandre Julliard committed
1717
/***********************************************************************
1718
 *           WritePrivateProfileStructW (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1719
 */
1720 1721
BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
                                        LPVOID buf, UINT bufsize, LPCWSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1722
{
1723
    BOOL ret = FALSE;
1724
    LPBYTE binbuf;
1725
    LPWSTR outstring, p;
1726
    DWORD sum = 0;
1727 1728

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

1731
    /* allocate string buffer for hex chars + checksum hex char + '\0' */
1732
    outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
    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';

1744
    RtlEnterCriticalSection( &PROFILE_CritSect );
1745

1746
    if (PROFILE_Open( filename, TRUE )) {
1747
        ret = PROFILE_SetString( section, key, outstring, FALSE);
1748 1749
        PROFILE_FlushFile();
    }
1750

1751
    RtlLeaveCriticalSection( &PROFILE_CritSect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1752

1753
    HeapFree( GetProcessHeap(), 0, outstring );
1754

1755
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1756 1757
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1758
/***********************************************************************
1759
 *           WritePrivateProfileStructA (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1760
 */
1761 1762
BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
					LPVOID buf, UINT bufsize, LPCSTR filename)
Alexandre Julliard's avatar
Alexandre Julliard committed
1763
{
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
    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
1777

1778 1779 1780
    RtlFreeUnicodeString(&sectionW);
    RtlFreeUnicodeString(&keyW);
    RtlFreeUnicodeString(&filenameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
1781 1782 1783
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1784

1785 1786 1787 1788 1789 1790 1791 1792 1793
/***********************************************************************
 *           OpenProfileUserMapping   (KERNEL32.@)
 */
BOOL WINAPI OpenProfileUserMapping(void) {
    FIXME("(), stub!\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}

1794
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1795
 *           CloseProfileUserMapping   (KERNEL32.@)
1796 1797 1798 1799 1800 1801
 */
BOOL WINAPI CloseProfileUserMapping(void) {
    FIXME("(), stub!\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}