#ifdef guard the declaration of type IID to be compatible with
[wine.git] / dlls / kernel / profile.c
blobca537465ab7e43fd6d56b350ac8727dbb96afc4c
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <string.h>
26 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnls.h"
31 #include "winerror.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "wine/winbase16.h"
35 #include "file.h"
36 #include "wine/unicode.h"
37 #include "wine/server.h"
38 #include "wine/library.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(profile);
43 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
45 typedef enum
47 ENCODING_ANSI = 1,
48 ENCODING_UTF8,
49 ENCODING_UTF16LE,
50 ENCODING_UTF16BE
51 } ENCODING;
53 typedef struct tagPROFILEKEY
55 WCHAR *value;
56 struct tagPROFILEKEY *next;
57 WCHAR name[1];
58 } PROFILEKEY;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY *key;
63 struct tagPROFILESECTION *next;
64 WCHAR name[1];
65 } PROFILESECTION;
68 typedef struct
70 BOOL changed;
71 PROFILESECTION *section;
72 WCHAR *filename;
73 FILETIME LastWriteTime;
74 ENCODING encoding;
75 } PROFILE;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR emptystringW[] = {0};
91 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect;
94 static CRITICAL_SECTION_DEBUG critsect_debug =
96 0, 0, &PROFILE_CritSect,
97 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
98 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
100 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
102 static const char hex[16] = "0123456789ABCDEF";
104 /***********************************************************************
105 * PROFILE_CopyEntry
107 * Copy the content of an entry into a buffer, removing quotes, and possibly
108 * translating environment variables.
110 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
111 BOOL strip_quote )
113 WCHAR quote = '\0';
115 if(!buffer) return;
117 if (strip_quote && ((*value == '\'') || (*value == '\"')))
119 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
122 lstrcpynW( buffer, value, len );
123 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
126 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
127 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
129 int i;
130 USHORT * shortbuffer = (USHORT *)buffer;
131 for (i = 0; i < len; i++)
132 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
135 /* writes any necessary encoding marker to the file */
136 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
138 DWORD dwBytesWritten;
139 DWORD bom;
140 switch (encoding)
142 case ENCODING_ANSI:
143 break;
144 case ENCODING_UTF8:
145 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
146 break;
147 case ENCODING_UTF16LE:
148 bom = 0xFEFF;
149 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
150 break;
151 case ENCODING_UTF16BE:
152 bom = 0xFFFE;
153 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
154 break;
158 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
160 char write_buffer[PROFILE_MAX_LINE_LEN];
161 DWORD dwBytesWritten;
163 TRACE("writing: %s\n", debugstr_wn(szLine, len));
165 switch (encoding)
167 case ENCODING_ANSI:
168 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
169 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
170 break;
171 case ENCODING_UTF8:
172 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
173 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
174 break;
175 case ENCODING_UTF16LE:
176 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
177 break;
178 case ENCODING_UTF16BE:
179 PROFILE_ByteSwapShortBuffer(szLine, len);
180 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
181 break;
182 default:
183 FIXME("encoding type %d not implemented\n", encoding);
187 /***********************************************************************
188 * PROFILE_Save
190 * Save a profile tree to a file.
192 static void PROFILE_Save( HANDLE hFile, PROFILESECTION *section, ENCODING encoding )
194 static const WCHAR wSectionFormat[] = {'\r','\n','[','%','s',']','\r','\n',0};
195 static const WCHAR wNameFormat[] = {'%','s',0};
196 static const WCHAR wValueFormat[] = {'=','%','s',0};
197 static const WCHAR wNewLine[] = {'\r','\n',0};
198 PROFILEKEY *key;
199 WCHAR szLine[PROFILE_MAX_LINE_LEN];
200 int len = 0;
202 PROFILE_WriteMarker(hFile, encoding);
204 for ( ; section; section = section->next)
206 if (section->name[0])
208 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wSectionFormat, section->name );
209 PROFILE_WriteLine( hFile, szLine, len, encoding );
210 len = 0;
213 for (key = section->key; key; key = key->next)
215 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNameFormat, key->name );
216 if (key->value)
217 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wValueFormat, key->value );
218 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNewLine );
219 PROFILE_WriteLine( hFile, szLine, len, encoding );
220 len = 0;
226 /***********************************************************************
227 * PROFILE_Free
229 * Free a profile tree.
231 static void PROFILE_Free( PROFILESECTION *section )
233 PROFILESECTION *next_section;
234 PROFILEKEY *key, *next_key;
236 for ( ; section; section = next_section)
238 for (key = section->key; key; key = next_key)
240 next_key = key->next;
241 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
242 HeapFree( GetProcessHeap(), 0, key );
244 next_section = section->next;
245 HeapFree( GetProcessHeap(), 0, section );
249 /* returns 1 if a character white space else 0 */
250 static inline int PROFILE_isspaceW(WCHAR c)
252 if (isspaceW(c)) return 1;
253 if (c=='\r' || c==0x1a) return 1;
254 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
255 return 0;
258 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
260 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
261 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
262 IS_TEXT_UNICODE_ODD_LENGTH;
263 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
265 *len = sizeof(bom_utf8);
266 return ENCODING_UTF8;
268 RtlIsTextUnicode((void *)buffer, *len, &flags);
269 if (flags & IS_TEXT_UNICODE_SIGNATURE)
271 *len = sizeof(WCHAR);
272 return ENCODING_UTF16LE;
274 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
276 *len = sizeof(WCHAR);
277 return ENCODING_UTF16BE;
279 *len = 0;
280 return ENCODING_ANSI;
283 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
285 return memchrW(szStart, '\n', szEnd - szStart);
288 /***********************************************************************
289 * PROFILE_Load
291 * Load a profile tree from a file.
293 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
295 void *pBuffer;
296 WCHAR * szFile;
297 const WCHAR *szLineStart, *szLineEnd;
298 const WCHAR *szValueStart, *szNameEnd, *szEnd;
299 int line = 0, len;
300 PROFILESECTION *section, *first_section;
301 PROFILESECTION **next_section;
302 PROFILEKEY *key, *prev_key, **next_key;
303 DWORD dwFileSize;
305 TRACE("%p\n", hFile);
307 dwFileSize = GetFileSize(hFile, NULL);
308 if (dwFileSize == INVALID_FILE_SIZE)
309 return NULL;
311 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
312 if (!pBuffer) return NULL;
314 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
316 HeapFree(GetProcessHeap(), 0, pBuffer);
317 WARN("Error %ld reading file\n", GetLastError());
318 return NULL;
320 len = dwFileSize;
321 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
322 /* len is set to the number of bytes in the character marker.
323 * we want to skip these bytes */
324 pBuffer = (char *)pBuffer + len;
325 dwFileSize -= len;
326 switch (*pEncoding)
328 case ENCODING_ANSI:
329 TRACE("ANSI encoding\n");
331 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
332 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
333 if (!szFile)
335 HeapFree(GetProcessHeap(), 0, pBuffer);
336 return NULL;
338 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
339 szEnd = szFile + len;
340 break;
341 case ENCODING_UTF8:
342 TRACE("UTF8 encoding\n");
344 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
345 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
346 if (!szFile)
348 HeapFree(GetProcessHeap(), 0, pBuffer);
349 return NULL;
351 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
352 szEnd = szFile + len;
353 break;
354 case ENCODING_UTF16LE:
355 TRACE("UTF16 Little Endian encoding\n");
356 szFile = (WCHAR *)pBuffer + 1;
357 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
358 break;
359 case ENCODING_UTF16BE:
360 TRACE("UTF16 Big Endian encoding\n");
361 szFile = (WCHAR *)pBuffer + 1;
362 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
363 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
364 break;
365 default:
366 FIXME("encoding type %d not implemented\n", *pEncoding);
367 HeapFree(GetProcessHeap(), 0, pBuffer);
368 return NULL;
371 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
372 if(first_section == NULL)
374 if (szFile != pBuffer)
375 HeapFree(GetProcessHeap(), 0, szFile);
376 HeapFree(GetProcessHeap(), 0, pBuffer);
377 return NULL;
379 first_section->name[0] = 0;
380 first_section->key = NULL;
381 first_section->next = NULL;
382 next_section = &first_section->next;
383 next_key = &first_section->key;
384 prev_key = NULL;
385 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
387 while (TRUE)
389 szLineStart = szLineEnd + 1;
390 if (szLineStart >= szEnd)
391 break;
392 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
393 if (!szLineEnd)
394 szLineEnd = szEnd;
395 line++;
397 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
399 if (szLineStart >= szLineEnd) continue;
401 if (*szLineStart == '[') /* section start */
403 const WCHAR * szSectionEnd;
404 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
406 WARN("Invalid section header at line %d: %s\n",
407 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
409 else
411 szLineStart++;
412 len = (int)(szSectionEnd - szLineStart);
413 /* no need to allocate +1 for NULL terminating character as
414 * already included in structure */
415 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
416 break;
417 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
418 section->name[len] = '\0';
419 section->key = NULL;
420 section->next = NULL;
421 *next_section = section;
422 next_section = &section->next;
423 next_key = &section->key;
424 prev_key = NULL;
426 TRACE("New section: %s\n", debugstr_w(section->name));
428 continue;
432 /* get rid of white space at the end of the line */
433 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
435 /* line end should be pointing to character *after* the last wanted character */
436 szLineEnd++;
438 /* get rid of white space after the name and before the start
439 * of the value */
440 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
442 szNameEnd = szValueStart - 1;
443 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
444 szValueStart++;
445 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
447 if (!szNameEnd)
448 szNameEnd = szLineEnd - 1;
449 /* name end should be pointing to character *after* the last wanted character */
450 szNameEnd++;
452 len = (int)(szNameEnd - szLineStart);
454 if (len || !prev_key || *prev_key->name)
456 /* no need to allocate +1 for NULL terminating character as
457 * already included in structure */
458 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
459 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
460 key->name[len] = '\0';
461 if (szValueStart && szValueStart < szLineEnd)
463 len = (int)(szLineEnd - szValueStart);
464 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
465 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
466 key->value[len] = '\0';
468 else key->value = NULL;
470 key->next = NULL;
471 *next_key = key;
472 next_key = &key->next;
473 prev_key = key;
475 TRACE("New key: name=%s, value=%s\n",
476 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
479 if (szFile != pBuffer)
480 HeapFree(GetProcessHeap(), 0, szFile);
481 HeapFree(GetProcessHeap(), 0, pBuffer);
482 return first_section;
486 /***********************************************************************
487 * PROFILE_DeleteSection
489 * Delete a section from a profile tree.
491 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
493 while (*section)
495 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
497 PROFILESECTION *to_del = *section;
498 *section = to_del->next;
499 to_del->next = NULL;
500 PROFILE_Free( to_del );
501 return TRUE;
503 section = &(*section)->next;
505 return FALSE;
509 /***********************************************************************
510 * PROFILE_DeleteKey
512 * Delete a key from a profile tree.
514 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
515 LPCWSTR section_name, LPCWSTR key_name )
517 while (*section)
519 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
521 PROFILEKEY **key = &(*section)->key;
522 while (*key)
524 if (!strcmpiW( (*key)->name, key_name ))
526 PROFILEKEY *to_del = *key;
527 *key = to_del->next;
528 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
529 HeapFree( GetProcessHeap(), 0, to_del );
530 return TRUE;
532 key = &(*key)->next;
535 section = &(*section)->next;
537 return FALSE;
541 /***********************************************************************
542 * PROFILE_DeleteAllKeys
544 * Delete all keys from a profile tree.
546 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
548 PROFILESECTION **section= &CurProfile->section;
549 while (*section)
551 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
553 PROFILEKEY **key = &(*section)->key;
554 while (*key)
556 PROFILEKEY *to_del = *key;
557 *key = to_del->next;
558 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
559 HeapFree( GetProcessHeap(), 0, to_del );
560 CurProfile->changed =TRUE;
563 section = &(*section)->next;
568 /***********************************************************************
569 * PROFILE_Find
571 * Find a key in a profile tree, optionally creating it.
573 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
574 LPCWSTR key_name, BOOL create, BOOL create_always )
576 LPCWSTR p;
577 int seclen, keylen;
579 while (PROFILE_isspaceW(*section_name)) section_name++;
580 p = section_name + strlenW(section_name) - 1;
581 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
582 seclen = p - section_name + 1;
584 while (PROFILE_isspaceW(*key_name)) key_name++;
585 p = key_name + strlenW(key_name) - 1;
586 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
587 keylen = p - key_name + 1;
589 while (*section)
591 if ( ((*section)->name[0])
592 && (!(strncmpiW( (*section)->name, section_name, seclen )))
593 && (((*section)->name)[seclen] == '\0') )
595 PROFILEKEY **key = &(*section)->key;
597 while (*key)
599 /* If create_always is FALSE then we check if the keyname
600 * already exists. Otherwise we add it regardless of its
601 * existence, to allow keys to be added more than once in
602 * some cases.
604 if(!create_always)
606 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
607 && (((*key)->name)[keylen] == '\0') )
608 return *key;
610 key = &(*key)->next;
612 if (!create) return NULL;
613 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
614 return NULL;
615 strcpyW( (*key)->name, key_name );
616 (*key)->value = NULL;
617 (*key)->next = NULL;
618 return *key;
620 section = &(*section)->next;
622 if (!create) return NULL;
623 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
624 if(*section == NULL) return NULL;
625 strcpyW( (*section)->name, section_name );
626 (*section)->next = NULL;
627 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
628 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
630 HeapFree(GetProcessHeap(), 0, *section);
631 return NULL;
633 strcpyW( (*section)->key->name, key_name );
634 (*section)->key->value = NULL;
635 (*section)->key->next = NULL;
636 return (*section)->key;
640 /***********************************************************************
641 * PROFILE_FlushFile
643 * Flush the current profile to disk if changed.
645 static BOOL PROFILE_FlushFile(void)
647 HANDLE hFile = NULL;
648 FILETIME LastWriteTime;
650 if(!CurProfile)
652 WARN("No current profile!\n");
653 return FALSE;
656 if (!CurProfile->changed) return TRUE;
658 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
660 if (hFile == INVALID_HANDLE_VALUE)
662 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
663 return FALSE;
666 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
667 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
668 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
669 CurProfile->LastWriteTime=LastWriteTime;
670 CloseHandle( hFile );
671 CurProfile->changed = FALSE;
672 return TRUE;
676 /***********************************************************************
677 * PROFILE_ReleaseFile
679 * Flush the current profile to disk and remove it from the cache.
681 static void PROFILE_ReleaseFile(void)
683 PROFILE_FlushFile();
684 PROFILE_Free( CurProfile->section );
685 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
686 CurProfile->changed = FALSE;
687 CurProfile->section = NULL;
688 CurProfile->filename = NULL;
689 CurProfile->encoding = ENCODING_ANSI;
690 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
694 /***********************************************************************
695 * PROFILE_Open
697 * Open a profile file, checking the cached file first.
699 static BOOL PROFILE_Open( LPCWSTR filename )
701 WCHAR windirW[MAX_PATH];
702 WCHAR buffer[MAX_PATH];
703 HANDLE hFile = INVALID_HANDLE_VALUE;
704 FILETIME LastWriteTime;
705 int i,j;
706 PROFILE *tempProfile;
708 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
710 /* First time around */
712 if(!CurProfile)
713 for(i=0;i<N_CACHED_PROFILES;i++)
715 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
716 if(MRUProfile[i] == NULL) break;
717 MRUProfile[i]->changed=FALSE;
718 MRUProfile[i]->section=NULL;
719 MRUProfile[i]->filename=NULL;
720 MRUProfile[i]->encoding=ENCODING_ANSI;
721 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
724 GetWindowsDirectoryW( windirW, MAX_PATH );
726 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
727 !strchrW(filename, '\\') && !strchrW(filename, '/'))
729 static const WCHAR wszSeparator[] = {'\\', 0};
730 strcpyW(buffer, windirW);
731 strcatW(buffer, wszSeparator);
732 strcatW(buffer, filename);
734 else
736 LPWSTR dummy;
737 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
740 TRACE("path: %s\n", debugstr_w(buffer));
742 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
744 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
746 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
747 return FALSE;
750 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
752 for(i=0;i<N_CACHED_PROFILES;i++)
754 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
756 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
757 if(i)
759 PROFILE_FlushFile();
760 tempProfile=MRUProfile[i];
761 for(j=i;j>0;j--)
762 MRUProfile[j]=MRUProfile[j-1];
763 CurProfile=tempProfile;
765 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
766 TRACE("(%s): already opened (mru=%d)\n",
767 debugstr_w(buffer), i );
768 else
769 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
770 debugstr_w(buffer), i );
771 CloseHandle(hFile);
772 return TRUE;
776 /* Flush the old current profile */
777 PROFILE_FlushFile();
779 /* Make the oldest profile the current one only in order to get rid of it */
780 if(i==N_CACHED_PROFILES)
782 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
783 for(i=N_CACHED_PROFILES-1;i>0;i--)
784 MRUProfile[i]=MRUProfile[i-1];
785 CurProfile=tempProfile;
787 if(CurProfile->filename) PROFILE_ReleaseFile();
789 /* OK, now that CurProfile is definitely free we assign it our new file */
790 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
791 strcpyW( CurProfile->filename, buffer );
793 if (hFile != INVALID_HANDLE_VALUE)
795 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
796 CloseHandle(hFile);
797 memcpy(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME));
799 else
801 /* Does not exist yet, we will create it in PROFILE_FlushFile */
802 WARN("profile file %s not found\n", debugstr_w(buffer) );
804 return TRUE;
808 /***********************************************************************
809 * PROFILE_GetSection
811 * Returns all keys of a section.
812 * If return_values is TRUE, also include the corresponding values.
814 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
815 LPWSTR buffer, UINT len, BOOL return_values )
817 PROFILEKEY *key;
819 if(!buffer) return 0;
821 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
823 while (section)
825 if (section->name[0] && !strcmpiW( section->name, section_name ))
827 UINT oldlen = len;
828 for (key = section->key; key; key = key->next)
830 if (len <= 2) break;
831 if (!*key->name) continue; /* Skip empty lines */
832 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
833 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
834 len -= strlenW(buffer) + 1;
835 buffer += strlenW(buffer) + 1;
836 if (len < 2)
837 break;
838 if (return_values && key->value) {
839 buffer[-1] = '=';
840 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
841 len -= strlenW(buffer) + 1;
842 buffer += strlenW(buffer) + 1;
845 *buffer = '\0';
846 if (len <= 1)
847 /*If either lpszSection or lpszKey is NULL and the supplied
848 destination buffer is too small to hold all the strings,
849 the last string is truncated and followed by two null characters.
850 In this case, the return value is equal to cchReturnBuffer
851 minus two. */
853 buffer[-1] = '\0';
854 return oldlen - 2;
856 return oldlen - len;
858 section = section->next;
860 buffer[0] = buffer[1] = '\0';
861 return 0;
864 /* See GetPrivateProfileSectionNamesA for documentation */
865 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
867 LPWSTR buf;
868 UINT f,l;
869 PROFILESECTION *section;
871 TRACE("(%p, %d)\n", buffer, len);
873 if (!buffer || !len)
874 return 0;
875 if (len==1) {
876 *buffer='\0';
877 return 0;
880 f=len-1;
881 buf=buffer;
882 section = CurProfile->section;
883 while ((section!=NULL)) {
884 if (section->name[0]) {
885 l = strlenW(section->name)+1;
886 if (l > f) {
887 if (f>0) {
888 strncpyW(buf, section->name, f-1);
889 buf += f-1;
890 *buf++='\0';
892 *buf='\0';
893 return len-2;
895 strcpyW(buf, section->name);
896 buf += l;
897 f -= l;
899 section = section->next;
901 *buf='\0';
902 return buf-buffer;
906 /***********************************************************************
907 * PROFILE_GetString
909 * Get a profile string.
911 * Tests with GetPrivateProfileString16, W95a,
912 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
913 * section key_name def_val res buffer
914 * "set1" "1" "x" 43 [data]
915 * "set1" "1 " "x" 43 [data] (!)
916 * "set1" " 1 "' "x" 43 [data] (!)
917 * "set1" "" "x" 1 "x"
918 * "set1" "" "x " 1 "x" (!)
919 * "set1" "" " x " 3 " x" (!)
920 * "set1" NULL "x" 6 "1\02\03\0\0"
921 * "set1" "" "x" 1 "x"
922 * NULL "1" "x" 0 "" (!)
923 * "" "1" "x" 1 "x"
924 * NULL NULL "" 0 ""
928 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
929 LPCWSTR def_val, LPWSTR buffer, UINT len )
931 PROFILEKEY *key = NULL;
932 static const WCHAR empty_strW[] = { 0 };
934 if(!buffer) return 0;
936 if (!def_val) def_val = empty_strW;
937 if (key_name)
939 if (!key_name[0])
941 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
942 return 0;
944 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
945 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
946 len, TRUE );
947 TRACE("(%s,%s,%s): returning %s\n",
948 debugstr_w(section), debugstr_w(key_name),
949 debugstr_w(def_val), debugstr_w(buffer) );
950 return strlenW( buffer );
952 /* no "else" here ! */
953 if (section && section[0])
955 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
956 if (!buffer[0]) /* no luck -> def_val */
958 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
959 ret = strlenW(buffer);
961 return ret;
963 buffer[0] = '\0';
964 return 0;
968 /***********************************************************************
969 * PROFILE_SetString
971 * Set a profile string.
973 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
974 LPCWSTR value, BOOL create_always )
976 if (!key_name) /* Delete a whole section */
978 TRACE("(%s)\n", debugstr_w(section_name));
979 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
980 section_name );
981 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
982 this is not an error on application's level.*/
984 else if (!value) /* Delete a key */
986 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
987 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
988 section_name, key_name );
989 return TRUE; /* same error handling as above */
991 else /* Set the key value */
993 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
994 key_name, TRUE, create_always );
995 TRACE("(%s,%s,%s):\n",
996 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
997 if (!key) return FALSE;
998 if (key->value)
1000 /* strip the leading spaces. We can safely strip \n\r and
1001 * friends too, they should not happen here anyway. */
1002 while (PROFILE_isspaceW(*value)) value++;
1004 if (!strcmpW( key->value, value ))
1006 TRACE(" no change needed\n" );
1007 return TRUE; /* No change needed */
1009 TRACE(" replacing %s\n", debugstr_w(key->value) );
1010 HeapFree( GetProcessHeap(), 0, key->value );
1012 else TRACE(" creating key\n" );
1013 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1014 strcpyW( key->value, value );
1015 CurProfile->changed = TRUE;
1017 return TRUE;
1021 /********************* API functions **********************************/
1023 /***********************************************************************
1024 * GetProfileInt (KERNEL.57)
1026 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1028 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1032 /***********************************************************************
1033 * GetProfileIntA (KERNEL32.@)
1035 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1037 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1040 /***********************************************************************
1041 * GetProfileIntW (KERNEL32.@)
1043 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1045 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1049 * if allow_section_name_copy is TRUE, allow the copying :
1050 * - of Section names if 'section' is NULL
1051 * - of Keys in a Section if 'entry' is NULL
1052 * (see MSDN doc for GetPrivateProfileString)
1054 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1055 LPCWSTR def_val, LPWSTR buffer,
1056 UINT len, LPCWSTR filename,
1057 BOOL allow_section_name_copy )
1059 int ret;
1060 LPWSTR pDefVal = NULL;
1062 if (!filename)
1063 filename = wininiW;
1065 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1066 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1068 /* strip any trailing ' ' of def_val. */
1069 if (def_val)
1071 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1073 while (p > def_val)
1075 p--;
1076 if ((*p) != ' ')
1077 break;
1079 if (*p == ' ') /* ouch, contained trailing ' ' */
1081 int len = (int)(p - def_val);
1082 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1083 strncpyW(pDefVal, def_val, len);
1084 pDefVal[len] = '\0';
1087 if (!pDefVal)
1088 pDefVal = (LPWSTR)def_val;
1090 RtlEnterCriticalSection( &PROFILE_CritSect );
1092 if (PROFILE_Open( filename )) {
1093 if ((allow_section_name_copy) && (section == NULL))
1094 ret = PROFILE_GetSectionNames(buffer, len);
1095 else
1096 /* PROFILE_GetString already handles the 'entry == NULL' case */
1097 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1098 } else {
1099 lstrcpynW( buffer, pDefVal, len );
1100 ret = strlenW( buffer );
1103 RtlLeaveCriticalSection( &PROFILE_CritSect );
1105 if (pDefVal != def_val) /* allocated */
1106 HeapFree(GetProcessHeap(), 0, pDefVal);
1108 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1110 return ret;
1113 /***********************************************************************
1114 * GetPrivateProfileString (KERNEL.128)
1116 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1117 LPCSTR def_val, LPSTR buffer,
1118 UINT16 len, LPCSTR filename )
1120 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1121 LPWSTR bufferW;
1122 INT16 retW, ret = 0;
1124 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1125 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1126 else sectionW.Buffer = NULL;
1127 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1128 else entryW.Buffer = NULL;
1129 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1130 else def_valW.Buffer = NULL;
1131 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1132 else filenameW.Buffer = NULL;
1134 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1135 def_valW.Buffer, bufferW, len,
1136 filenameW.Buffer, FALSE );
1137 if (len)
1139 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1140 if (!ret)
1142 ret = len - 1;
1143 buffer[ret] = 0;
1145 else
1146 ret--; /* strip terminating 0 */
1149 RtlFreeUnicodeString(&sectionW);
1150 RtlFreeUnicodeString(&entryW);
1151 RtlFreeUnicodeString(&def_valW);
1152 RtlFreeUnicodeString(&filenameW);
1153 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1154 return ret;
1157 /***********************************************************************
1158 * GetPrivateProfileStringA (KERNEL32.@)
1160 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1161 LPCSTR def_val, LPSTR buffer,
1162 UINT len, LPCSTR filename )
1164 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1165 LPWSTR bufferW;
1166 INT retW, ret = 0;
1168 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1169 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1170 else sectionW.Buffer = NULL;
1171 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1172 else entryW.Buffer = NULL;
1173 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1174 else def_valW.Buffer = NULL;
1175 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1176 else filenameW.Buffer = NULL;
1178 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1179 def_valW.Buffer, bufferW, len,
1180 filenameW.Buffer);
1181 if (len)
1183 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1184 if (!ret)
1186 ret = len - 1;
1187 buffer[ret] = 0;
1189 else
1190 ret--; /* strip terminating 0 */
1193 RtlFreeUnicodeString(&sectionW);
1194 RtlFreeUnicodeString(&entryW);
1195 RtlFreeUnicodeString(&def_valW);
1196 RtlFreeUnicodeString(&filenameW);
1197 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1198 return ret;
1201 /***********************************************************************
1202 * GetPrivateProfileStringW (KERNEL32.@)
1204 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1205 LPCWSTR def_val, LPWSTR buffer,
1206 UINT len, LPCWSTR filename )
1208 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1210 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1211 buffer, len, filename, TRUE );
1214 /***********************************************************************
1215 * GetProfileString (KERNEL.58)
1217 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1218 LPSTR buffer, UINT16 len )
1220 return GetPrivateProfileString16( section, entry, def_val,
1221 buffer, len, "win.ini" );
1224 /***********************************************************************
1225 * GetProfileStringA (KERNEL32.@)
1227 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1228 LPSTR buffer, UINT len )
1230 return GetPrivateProfileStringA( section, entry, def_val,
1231 buffer, len, "win.ini" );
1234 /***********************************************************************
1235 * GetProfileStringW (KERNEL32.@)
1237 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1238 LPCWSTR def_val, LPWSTR buffer, UINT len )
1240 return GetPrivateProfileStringW( section, entry, def_val,
1241 buffer, len, wininiW );
1244 /***********************************************************************
1245 * WriteProfileString (KERNEL.59)
1247 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1248 LPCSTR string )
1250 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1253 /***********************************************************************
1254 * WriteProfileStringA (KERNEL32.@)
1256 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1257 LPCSTR string )
1259 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1262 /***********************************************************************
1263 * WriteProfileStringW (KERNEL32.@)
1265 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1266 LPCWSTR string )
1268 return WritePrivateProfileStringW( section, entry, string, wininiW );
1272 /***********************************************************************
1273 * GetPrivateProfileInt (KERNEL.127)
1275 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1276 INT16 def_val, LPCSTR filename )
1278 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1279 * here, but Win98SE doesn't care about this at all, so I deleted it.
1280 * AFAIR versions prior to Win9x had these limits, though. */
1281 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1284 /***********************************************************************
1285 * GetPrivateProfileIntW (KERNEL32.@)
1287 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1288 INT def_val, LPCWSTR filename )
1290 WCHAR buffer[30];
1291 UNICODE_STRING bufferW;
1292 INT len;
1293 ULONG result;
1295 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1296 buffer, sizeof(buffer)/sizeof(WCHAR),
1297 filename )))
1298 return def_val;
1300 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1302 /* FIXME: if entry can be found but it's empty, then Win16 is
1303 * supposed to return 0 instead of def_val ! Difficult/problematic
1304 * to implement (every other failure also returns zero buffer),
1305 * thus wait until testing framework avail for making sure nothing
1306 * else gets broken that way. */
1307 if (!buffer[0]) return (UINT)def_val;
1309 RtlInitUnicodeString( &bufferW, buffer );
1310 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1311 return result;
1314 /***********************************************************************
1315 * GetPrivateProfileIntA (KERNEL32.@)
1317 * FIXME: rewrite using unicode
1319 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1320 INT def_val, LPCSTR filename )
1322 UNICODE_STRING entryW, filenameW, sectionW;
1323 UINT res;
1324 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1325 else entryW.Buffer = NULL;
1326 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1327 else filenameW.Buffer = NULL;
1328 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1329 else sectionW.Buffer = NULL;
1330 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1331 filenameW.Buffer);
1332 RtlFreeUnicodeString(&sectionW);
1333 RtlFreeUnicodeString(&filenameW);
1334 RtlFreeUnicodeString(&entryW);
1335 return res;
1338 /***********************************************************************
1339 * GetPrivateProfileSection (KERNEL.418)
1341 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1342 UINT16 len, LPCSTR filename )
1344 return GetPrivateProfileSectionA( section, buffer, len, filename );
1347 /***********************************************************************
1348 * GetPrivateProfileSectionW (KERNEL32.@)
1350 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1351 DWORD len, LPCWSTR filename )
1353 int ret = 0;
1355 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1357 RtlEnterCriticalSection( &PROFILE_CritSect );
1359 if (PROFILE_Open( filename ))
1360 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1362 RtlLeaveCriticalSection( &PROFILE_CritSect );
1364 return ret;
1367 /***********************************************************************
1368 * GetPrivateProfileSectionA (KERNEL32.@)
1370 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1371 DWORD len, LPCSTR filename )
1373 UNICODE_STRING sectionW, filenameW;
1374 LPWSTR bufferW;
1375 INT retW, ret = 0;
1377 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1378 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1379 else sectionW.Buffer = NULL;
1380 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1381 else filenameW.Buffer = NULL;
1383 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1384 if (len > 2)
1386 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1387 if (ret > 2)
1388 ret -= 2;
1389 else
1391 ret = 0;
1392 buffer[len-2] = 0;
1393 buffer[len-1] = 0;
1396 else
1398 buffer[0] = 0;
1399 buffer[1] = 0;
1402 RtlFreeUnicodeString(&sectionW);
1403 RtlFreeUnicodeString(&filenameW);
1404 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1405 return ret;
1408 /***********************************************************************
1409 * GetProfileSection (KERNEL.419)
1411 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1413 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1416 /***********************************************************************
1417 * GetProfileSectionA (KERNEL32.@)
1419 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1421 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1424 /***********************************************************************
1425 * GetProfileSectionW (KERNEL32.@)
1427 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1429 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1433 /***********************************************************************
1434 * WritePrivateProfileString (KERNEL.129)
1436 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1437 LPCSTR string, LPCSTR filename )
1439 return WritePrivateProfileStringA(section,entry,string,filename);
1442 /***********************************************************************
1443 * WritePrivateProfileStringW (KERNEL32.@)
1445 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1446 LPCWSTR string, LPCWSTR filename )
1448 BOOL ret = FALSE;
1450 RtlEnterCriticalSection( &PROFILE_CritSect );
1452 if (PROFILE_Open( filename ))
1454 if (!section && !entry && !string) /* documented "file flush" case */
1456 PROFILE_FlushFile();
1457 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1459 else {
1460 if (!section) {
1461 FIXME("(NULL?,%s,%s,%s)?\n",
1462 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1463 } else {
1464 ret = PROFILE_SetString( section, entry, string, FALSE);
1465 PROFILE_FlushFile();
1470 RtlLeaveCriticalSection( &PROFILE_CritSect );
1471 return ret;
1474 /***********************************************************************
1475 * WritePrivateProfileStringA (KERNEL32.@)
1477 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1478 LPCSTR string, LPCSTR filename )
1480 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1481 BOOL ret;
1483 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1484 else sectionW.Buffer = NULL;
1485 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1486 else entryW.Buffer = NULL;
1487 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1488 else stringW.Buffer = NULL;
1489 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1490 else filenameW.Buffer = NULL;
1492 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1493 stringW.Buffer, filenameW.Buffer);
1494 RtlFreeUnicodeString(&sectionW);
1495 RtlFreeUnicodeString(&entryW);
1496 RtlFreeUnicodeString(&stringW);
1497 RtlFreeUnicodeString(&filenameW);
1498 return ret;
1501 /***********************************************************************
1502 * WritePrivateProfileSection (KERNEL.416)
1504 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1505 LPCSTR string, LPCSTR filename )
1507 return WritePrivateProfileSectionA( section, string, filename );
1510 /***********************************************************************
1511 * WritePrivateProfileSectionW (KERNEL32.@)
1513 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1514 LPCWSTR string, LPCWSTR filename )
1516 BOOL ret = FALSE;
1517 LPWSTR p;
1519 RtlEnterCriticalSection( &PROFILE_CritSect );
1521 if (PROFILE_Open( filename )) {
1522 if (!section && !string)
1523 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1524 else if (!string) {/* delete the named section*/
1525 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1526 PROFILE_FlushFile();
1527 } else {
1528 PROFILE_DeleteAllKeys(section);
1529 ret = TRUE;
1530 while(*string) {
1531 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1532 strcpyW( buf, string );
1533 if((p = strchrW( buf, '='))) {
1534 *p='\0';
1535 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1537 HeapFree( GetProcessHeap(), 0, buf );
1538 string += strlenW(string)+1;
1540 PROFILE_FlushFile();
1544 RtlLeaveCriticalSection( &PROFILE_CritSect );
1545 return ret;
1548 /***********************************************************************
1549 * WritePrivateProfileSectionA (KERNEL32.@)
1551 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1552 LPCSTR string, LPCSTR filename)
1555 UNICODE_STRING sectionW, filenameW;
1556 LPWSTR stringW;
1557 BOOL ret;
1559 if (string)
1561 INT lenA, lenW;
1562 LPCSTR p = string;
1564 while(*p) p += strlen(p) + 1;
1565 lenA = p - string + 1;
1566 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1567 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1568 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1570 else stringW = NULL;
1571 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1572 else sectionW.Buffer = NULL;
1573 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1574 else filenameW.Buffer = NULL;
1576 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1578 HeapFree(GetProcessHeap(), 0, stringW);
1579 RtlFreeUnicodeString(&sectionW);
1580 RtlFreeUnicodeString(&filenameW);
1581 return ret;
1584 /***********************************************************************
1585 * WriteProfileSection (KERNEL.417)
1587 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1589 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1592 /***********************************************************************
1593 * WriteProfileSectionA (KERNEL32.@)
1595 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1598 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1601 /***********************************************************************
1602 * WriteProfileSectionW (KERNEL32.@)
1604 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1606 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1609 /***********************************************************************
1610 * GetPrivateProfileSectionNames (KERNEL.143)
1612 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1613 LPCSTR filename )
1615 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1619 /***********************************************************************
1620 * GetProfileSectionNames (KERNEL.142)
1622 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1625 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1629 /***********************************************************************
1630 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1632 * Returns the section names contained in the specified file.
1633 * FIXME: Where do we find this file when the path is relative?
1634 * The section names are returned as a list of strings with an extra
1635 * '\0' to mark the end of the list. Except for that the behavior
1636 * depends on the Windows version.
1638 * Win95:
1639 * - if the buffer is 0 or 1 character long then it is as if it was of
1640 * infinite length.
1641 * - otherwise, if the buffer is to small only the section names that fit
1642 * are returned.
1643 * - note that this means if the buffer was to small to return even just
1644 * the first section name then a single '\0' will be returned.
1645 * - the return value is the number of characters written in the buffer,
1646 * except if the buffer was too smal in which case len-2 is returned
1648 * Win2000:
1649 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1650 * '\0' and the return value is 0
1651 * - otherwise if the buffer is too small then the first section name that
1652 * does not fit is truncated so that the string list can be terminated
1653 * correctly (double '\0')
1654 * - the return value is the number of characters written in the buffer
1655 * except for the trailing '\0'. If the buffer is too small, then the
1656 * return value is len-2
1657 * - Win2000 has a bug that triggers when the section names and the
1658 * trailing '\0' fit exactly in the buffer. In that case the trailing
1659 * '\0' is missing.
1661 * Wine implements the observed Win2000 behavior (except for the bug).
1663 * Note that when the buffer is big enough then the return value may be any
1664 * value between 1 and len-1 (or len in Win95), including len-2.
1666 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1667 LPCWSTR filename)
1669 DWORD ret = 0;
1671 RtlEnterCriticalSection( &PROFILE_CritSect );
1673 if (PROFILE_Open( filename ))
1674 ret = PROFILE_GetSectionNames(buffer, size);
1676 RtlLeaveCriticalSection( &PROFILE_CritSect );
1678 return ret;
1682 /***********************************************************************
1683 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1685 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1686 LPCSTR filename)
1688 UNICODE_STRING filenameW;
1689 LPWSTR bufferW;
1690 INT retW, ret = 0;
1692 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1693 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1694 else filenameW.Buffer = NULL;
1696 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1697 if (retW && size)
1699 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1700 if (!ret)
1702 ret = size;
1703 buffer[size-1] = 0;
1707 RtlFreeUnicodeString(&filenameW);
1708 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1709 return ret;
1712 /***********************************************************************
1713 * GetPrivateProfileStruct (KERNEL.407)
1715 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1716 LPVOID buf, UINT16 len, LPCSTR filename)
1718 return GetPrivateProfileStructA( section, key, buf, len, filename );
1721 /***********************************************************************
1722 * GetPrivateProfileStructW (KERNEL32.@)
1724 * Should match Win95's behaviour pretty much
1726 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1727 LPVOID buf, UINT len, LPCWSTR filename)
1729 BOOL ret = FALSE;
1731 RtlEnterCriticalSection( &PROFILE_CritSect );
1733 if (PROFILE_Open( filename )) {
1734 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1735 if (k) {
1736 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1737 if (((strlenW(k->value) - 2) / 2) == len)
1739 LPWSTR end, p;
1740 BOOL valid = TRUE;
1741 WCHAR c;
1742 DWORD chksum = 0;
1744 end = k->value + strlenW(k->value); /* -> '\0' */
1745 /* check for invalid chars in ASCII coded hex string */
1746 for (p=k->value; p < end; p++)
1748 if (!isxdigitW(*p))
1750 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1751 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1752 valid = FALSE;
1753 break;
1756 if (valid)
1758 BOOL highnibble = TRUE;
1759 BYTE b = 0, val;
1760 LPBYTE binbuf = (LPBYTE)buf;
1762 end -= 2; /* don't include checksum in output data */
1763 /* translate ASCII hex format into binary data */
1764 for (p=k->value; p < end; p++)
1766 c = toupperW(*p);
1767 val = (c > '9') ?
1768 (c - 'A' + 10) : (c - '0');
1770 if (highnibble)
1771 b = val << 4;
1772 else
1774 b += val;
1775 *binbuf++ = b; /* feed binary data into output */
1776 chksum += b; /* calculate checksum */
1778 highnibble ^= 1; /* toggle */
1780 /* retrieve stored checksum value */
1781 c = toupperW(*p++);
1782 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1783 c = toupperW(*p);
1784 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1785 if (b == (chksum & 0xff)) /* checksums match ? */
1786 ret = TRUE;
1791 RtlLeaveCriticalSection( &PROFILE_CritSect );
1793 return ret;
1796 /***********************************************************************
1797 * GetPrivateProfileStructA (KERNEL32.@)
1799 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1800 LPVOID buffer, UINT len, LPCSTR filename)
1802 UNICODE_STRING sectionW, keyW, filenameW;
1803 INT ret;
1805 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1806 else sectionW.Buffer = NULL;
1807 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1808 else keyW.Buffer = NULL;
1809 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1810 else filenameW.Buffer = NULL;
1812 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1813 filenameW.Buffer);
1814 /* Do not translate binary data. */
1816 RtlFreeUnicodeString(&sectionW);
1817 RtlFreeUnicodeString(&keyW);
1818 RtlFreeUnicodeString(&filenameW);
1819 return ret;
1824 /***********************************************************************
1825 * WritePrivateProfileStruct (KERNEL.406)
1827 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1828 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1830 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1833 /***********************************************************************
1834 * WritePrivateProfileStructW (KERNEL32.@)
1836 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1837 LPVOID buf, UINT bufsize, LPCWSTR filename)
1839 BOOL ret = FALSE;
1840 LPBYTE binbuf;
1841 LPWSTR outstring, p;
1842 DWORD sum = 0;
1844 if (!section && !key && !buf) /* flush the cache */
1845 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1847 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1848 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1849 p = outstring;
1850 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1851 *p++ = hex[*binbuf >> 4];
1852 *p++ = hex[*binbuf & 0xf];
1853 sum += *binbuf;
1855 /* checksum is sum & 0xff */
1856 *p++ = hex[(sum & 0xf0) >> 4];
1857 *p++ = hex[sum & 0xf];
1858 *p++ = '\0';
1860 RtlEnterCriticalSection( &PROFILE_CritSect );
1862 if (PROFILE_Open( filename )) {
1863 ret = PROFILE_SetString( section, key, outstring, FALSE);
1864 PROFILE_FlushFile();
1867 RtlLeaveCriticalSection( &PROFILE_CritSect );
1869 HeapFree( GetProcessHeap(), 0, outstring );
1871 return ret;
1874 /***********************************************************************
1875 * WritePrivateProfileStructA (KERNEL32.@)
1877 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1878 LPVOID buf, UINT bufsize, LPCSTR filename)
1880 UNICODE_STRING sectionW, keyW, filenameW;
1881 INT ret;
1883 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1884 else sectionW.Buffer = NULL;
1885 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1886 else keyW.Buffer = NULL;
1887 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1888 else filenameW.Buffer = NULL;
1890 /* Do not translate binary data. */
1891 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1892 filenameW.Buffer);
1894 RtlFreeUnicodeString(&sectionW);
1895 RtlFreeUnicodeString(&keyW);
1896 RtlFreeUnicodeString(&filenameW);
1897 return ret;
1901 /***********************************************************************
1902 * WriteOutProfiles (KERNEL.315)
1904 void WINAPI WriteOutProfiles16(void)
1906 RtlEnterCriticalSection( &PROFILE_CritSect );
1907 PROFILE_FlushFile();
1908 RtlLeaveCriticalSection( &PROFILE_CritSect );
1911 /***********************************************************************
1912 * CloseProfileUserMapping (KERNEL32.@)
1914 BOOL WINAPI CloseProfileUserMapping(void) {
1915 FIXME("(), stub!\n");
1916 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1917 return FALSE;