Moved most remaining file functions to dlls/kernel.
[wine/multimedia.git] / dlls / kernel / profile.c
blob92dd1bf044668a879ff8bc90417c7f4c68f7d91c
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 "wine/unicode.h"
36 #include "wine/server.h"
37 #include "wine/library.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(profile);
42 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
44 typedef enum
46 ENCODING_ANSI = 1,
47 ENCODING_UTF8,
48 ENCODING_UTF16LE,
49 ENCODING_UTF16BE
50 } ENCODING;
52 typedef struct tagPROFILEKEY
54 WCHAR *value;
55 struct tagPROFILEKEY *next;
56 WCHAR name[1];
57 } PROFILEKEY;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
63 WCHAR name[1];
64 } PROFILESECTION;
67 typedef struct
69 BOOL changed;
70 PROFILESECTION *section;
71 WCHAR *filename;
72 FILETIME LastWriteTime;
73 ENCODING encoding;
74 } PROFILE;
77 #define N_CACHED_PROFILES 10
79 /* Cached profile files */
80 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
82 #define CurProfile (MRUProfile[0])
84 #define PROFILE_MAX_LINE_LEN 1024
86 /* Check for comments in profile */
87 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
89 static const WCHAR emptystringW[] = {0};
90 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
92 static CRITICAL_SECTION PROFILE_CritSect;
93 static CRITICAL_SECTION_DEBUG critsect_debug =
95 0, 0, &PROFILE_CritSect,
96 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
97 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
99 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
101 static const char hex[16] = "0123456789ABCDEF";
103 /***********************************************************************
104 * PROFILE_CopyEntry
106 * Copy the content of an entry into a buffer, removing quotes, and possibly
107 * translating environment variables.
109 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
110 BOOL strip_quote )
112 WCHAR quote = '\0';
114 if(!buffer) return;
116 if (strip_quote && ((*value == '\'') || (*value == '\"')))
118 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
121 lstrcpynW( buffer, value, len );
122 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
125 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
126 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
128 int i;
129 USHORT * shortbuffer = (USHORT *)buffer;
130 for (i = 0; i < len; i++)
131 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
134 /* writes any necessary encoding marker to the file */
135 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
137 DWORD dwBytesWritten;
138 DWORD bom;
139 switch (encoding)
141 case ENCODING_ANSI:
142 break;
143 case ENCODING_UTF8:
144 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
145 break;
146 case ENCODING_UTF16LE:
147 bom = 0xFEFF;
148 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
149 break;
150 case ENCODING_UTF16BE:
151 bom = 0xFFFE;
152 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
153 break;
157 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
159 char write_buffer[PROFILE_MAX_LINE_LEN];
160 DWORD dwBytesWritten;
162 TRACE("writing: %s\n", debugstr_wn(szLine, len));
164 switch (encoding)
166 case ENCODING_ANSI:
167 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
168 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
169 break;
170 case ENCODING_UTF8:
171 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, sizeof(write_buffer), NULL, NULL);
172 WriteFile(hFile, write_buffer, len * sizeof(char), &dwBytesWritten, NULL);
173 break;
174 case ENCODING_UTF16LE:
175 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
176 break;
177 case ENCODING_UTF16BE:
178 PROFILE_ByteSwapShortBuffer(szLine, len);
179 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
180 break;
181 default:
182 FIXME("encoding type %d not implemented\n", encoding);
186 /***********************************************************************
187 * PROFILE_Save
189 * Save a profile tree to a file.
191 static void PROFILE_Save( HANDLE hFile, PROFILESECTION *section, ENCODING encoding )
193 static const WCHAR wSectionFormat[] = {'\r','\n','[','%','s',']','\r','\n',0};
194 static const WCHAR wNameFormat[] = {'%','s',0};
195 static const WCHAR wValueFormat[] = {'=','%','s',0};
196 static const WCHAR wNewLine[] = {'\r','\n',0};
197 PROFILEKEY *key;
198 WCHAR szLine[PROFILE_MAX_LINE_LEN];
199 int len = 0;
201 PROFILE_WriteMarker(hFile, encoding);
203 for ( ; section; section = section->next)
205 if (section->name[0])
207 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wSectionFormat, section->name );
208 PROFILE_WriteLine( hFile, szLine, len, encoding );
209 len = 0;
212 for (key = section->key; key; key = key->next)
214 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNameFormat, key->name );
215 if (key->value)
216 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wValueFormat, key->value );
217 len += snprintfW( szLine + len, PROFILE_MAX_LINE_LEN - len, wNewLine );
218 PROFILE_WriteLine( hFile, szLine, len, encoding );
219 len = 0;
225 /***********************************************************************
226 * PROFILE_Free
228 * Free a profile tree.
230 static void PROFILE_Free( PROFILESECTION *section )
232 PROFILESECTION *next_section;
233 PROFILEKEY *key, *next_key;
235 for ( ; section; section = next_section)
237 for (key = section->key; key; key = next_key)
239 next_key = key->next;
240 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
241 HeapFree( GetProcessHeap(), 0, key );
243 next_section = section->next;
244 HeapFree( GetProcessHeap(), 0, section );
248 /* returns 1 if a character white space else 0 */
249 static inline int PROFILE_isspaceW(WCHAR c)
251 if (isspaceW(c)) return 1;
252 if (c=='\r' || c==0x1a) return 1;
253 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
254 return 0;
257 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
259 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
260 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
261 IS_TEXT_UNICODE_ODD_LENGTH;
262 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
264 *len = sizeof(bom_utf8);
265 return ENCODING_UTF8;
267 RtlIsTextUnicode((void *)buffer, *len, &flags);
268 if (flags & IS_TEXT_UNICODE_SIGNATURE)
270 *len = sizeof(WCHAR);
271 return ENCODING_UTF16LE;
273 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
275 *len = sizeof(WCHAR);
276 return ENCODING_UTF16BE;
278 *len = 0;
279 return ENCODING_ANSI;
282 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
284 return memchrW(szStart, '\n', szEnd - szStart);
287 /***********************************************************************
288 * PROFILE_Load
290 * Load a profile tree from a file.
292 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
294 void *pBuffer;
295 WCHAR * szFile;
296 const WCHAR *szLineStart, *szLineEnd;
297 const WCHAR *szValueStart, *szNameEnd, *szEnd;
298 int line = 0, len;
299 PROFILESECTION *section, *first_section;
300 PROFILESECTION **next_section;
301 PROFILEKEY *key, *prev_key, **next_key;
302 DWORD dwFileSize;
304 TRACE("%p\n", hFile);
306 dwFileSize = GetFileSize(hFile, NULL);
307 if (dwFileSize == INVALID_FILE_SIZE)
308 return NULL;
310 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
311 if (!pBuffer) return NULL;
313 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
315 HeapFree(GetProcessHeap(), 0, pBuffer);
316 WARN("Error %ld reading file\n", GetLastError());
317 return NULL;
319 len = dwFileSize;
320 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
321 /* len is set to the number of bytes in the character marker.
322 * we want to skip these bytes */
323 pBuffer = (char *)pBuffer + len;
324 dwFileSize -= len;
325 switch (*pEncoding)
327 case ENCODING_ANSI:
328 TRACE("ANSI encoding\n");
330 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
331 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
332 if (!szFile)
334 HeapFree(GetProcessHeap(), 0, pBuffer);
335 return NULL;
337 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
338 szEnd = szFile + len;
339 break;
340 case ENCODING_UTF8:
341 TRACE("UTF8 encoding\n");
343 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
344 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
345 if (!szFile)
347 HeapFree(GetProcessHeap(), 0, pBuffer);
348 return NULL;
350 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
351 szEnd = szFile + len;
352 break;
353 case ENCODING_UTF16LE:
354 TRACE("UTF16 Little Endian encoding\n");
355 szFile = (WCHAR *)pBuffer + 1;
356 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
357 break;
358 case ENCODING_UTF16BE:
359 TRACE("UTF16 Big Endian encoding\n");
360 szFile = (WCHAR *)pBuffer + 1;
361 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
362 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
363 break;
364 default:
365 FIXME("encoding type %d not implemented\n", *pEncoding);
366 HeapFree(GetProcessHeap(), 0, pBuffer);
367 return NULL;
370 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
371 if(first_section == NULL)
373 if (szFile != pBuffer)
374 HeapFree(GetProcessHeap(), 0, szFile);
375 HeapFree(GetProcessHeap(), 0, pBuffer);
376 return NULL;
378 first_section->name[0] = 0;
379 first_section->key = NULL;
380 first_section->next = NULL;
381 next_section = &first_section->next;
382 next_key = &first_section->key;
383 prev_key = NULL;
384 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
386 while (TRUE)
388 szLineStart = szLineEnd + 1;
389 if (szLineStart >= szEnd)
390 break;
391 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
392 if (!szLineEnd)
393 szLineEnd = szEnd;
394 line++;
396 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
398 if (szLineStart >= szLineEnd) continue;
400 if (*szLineStart == '[') /* section start */
402 const WCHAR * szSectionEnd;
403 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
405 WARN("Invalid section header at line %d: %s\n",
406 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
408 else
410 szLineStart++;
411 len = (int)(szSectionEnd - szLineStart);
412 /* no need to allocate +1 for NULL terminating character as
413 * already included in structure */
414 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
415 break;
416 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
417 section->name[len] = '\0';
418 section->key = NULL;
419 section->next = NULL;
420 *next_section = section;
421 next_section = &section->next;
422 next_key = &section->key;
423 prev_key = NULL;
425 TRACE("New section: %s\n", debugstr_w(section->name));
427 continue;
431 /* get rid of white space at the end of the line */
432 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
434 /* line end should be pointing to character *after* the last wanted character */
435 szLineEnd++;
437 /* get rid of white space after the name and before the start
438 * of the value */
439 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
441 szNameEnd = szValueStart - 1;
442 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
443 szValueStart++;
444 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
446 if (!szNameEnd)
447 szNameEnd = szLineEnd - 1;
448 /* name end should be pointing to character *after* the last wanted character */
449 szNameEnd++;
451 len = (int)(szNameEnd - szLineStart);
453 if (len || !prev_key || *prev_key->name)
455 /* no need to allocate +1 for NULL terminating character as
456 * already included in structure */
457 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
458 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
459 key->name[len] = '\0';
460 if (szValueStart)
462 len = (int)(szLineEnd - szValueStart);
463 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
464 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
465 key->value[len] = '\0';
467 else key->value = NULL;
469 key->next = NULL;
470 *next_key = key;
471 next_key = &key->next;
472 prev_key = key;
474 TRACE("New key: name=%s, value=%s\n",
475 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
478 if (szFile != pBuffer)
479 HeapFree(GetProcessHeap(), 0, szFile);
480 HeapFree(GetProcessHeap(), 0, pBuffer);
481 return first_section;
485 /***********************************************************************
486 * PROFILE_DeleteSection
488 * Delete a section from a profile tree.
490 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
492 while (*section)
494 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
496 PROFILESECTION *to_del = *section;
497 *section = to_del->next;
498 to_del->next = NULL;
499 PROFILE_Free( to_del );
500 return TRUE;
502 section = &(*section)->next;
504 return FALSE;
508 /***********************************************************************
509 * PROFILE_DeleteKey
511 * Delete a key from a profile tree.
513 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
514 LPCWSTR section_name, LPCWSTR key_name )
516 while (*section)
518 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
520 PROFILEKEY **key = &(*section)->key;
521 while (*key)
523 if (!strcmpiW( (*key)->name, key_name ))
525 PROFILEKEY *to_del = *key;
526 *key = to_del->next;
527 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
528 HeapFree( GetProcessHeap(), 0, to_del );
529 return TRUE;
531 key = &(*key)->next;
534 section = &(*section)->next;
536 return FALSE;
540 /***********************************************************************
541 * PROFILE_DeleteAllKeys
543 * Delete all keys from a profile tree.
545 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
547 PROFILESECTION **section= &CurProfile->section;
548 while (*section)
550 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
552 PROFILEKEY **key = &(*section)->key;
553 while (*key)
555 PROFILEKEY *to_del = *key;
556 *key = to_del->next;
557 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
558 HeapFree( GetProcessHeap(), 0, to_del );
559 CurProfile->changed =TRUE;
562 section = &(*section)->next;
567 /***********************************************************************
568 * PROFILE_Find
570 * Find a key in a profile tree, optionally creating it.
572 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
573 LPCWSTR key_name, BOOL create, BOOL create_always )
575 LPCWSTR p;
576 int seclen, keylen;
578 while (PROFILE_isspaceW(*section_name)) section_name++;
579 p = section_name + strlenW(section_name) - 1;
580 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
581 seclen = p - section_name + 1;
583 while (PROFILE_isspaceW(*key_name)) key_name++;
584 p = key_name + strlenW(key_name) - 1;
585 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
586 keylen = p - key_name + 1;
588 while (*section)
590 if ( ((*section)->name[0])
591 && (!(strncmpiW( (*section)->name, section_name, seclen )))
592 && (((*section)->name)[seclen] == '\0') )
594 PROFILEKEY **key = &(*section)->key;
596 while (*key)
598 /* If create_always is FALSE then we check if the keyname
599 * already exists. Otherwise we add it regardless of its
600 * existence, to allow keys to be added more than once in
601 * some cases.
603 if(!create_always)
605 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
606 && (((*key)->name)[keylen] == '\0') )
607 return *key;
609 key = &(*key)->next;
611 if (!create) return NULL;
612 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
613 return NULL;
614 strcpyW( (*key)->name, key_name );
615 (*key)->value = NULL;
616 (*key)->next = NULL;
617 return *key;
619 section = &(*section)->next;
621 if (!create) return NULL;
622 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
623 if(*section == NULL) return NULL;
624 strcpyW( (*section)->name, section_name );
625 (*section)->next = NULL;
626 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
627 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
629 HeapFree(GetProcessHeap(), 0, *section);
630 return NULL;
632 strcpyW( (*section)->key->name, key_name );
633 (*section)->key->value = NULL;
634 (*section)->key->next = NULL;
635 return (*section)->key;
639 /***********************************************************************
640 * PROFILE_FlushFile
642 * Flush the current profile to disk if changed.
644 static BOOL PROFILE_FlushFile(void)
646 HANDLE hFile = NULL;
647 FILETIME LastWriteTime;
649 if(!CurProfile)
651 WARN("No current profile!\n");
652 return FALSE;
655 if (!CurProfile->changed) return TRUE;
657 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
659 if (hFile == INVALID_HANDLE_VALUE)
661 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
662 return FALSE;
665 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
666 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
667 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
668 CurProfile->LastWriteTime=LastWriteTime;
669 CloseHandle( hFile );
670 CurProfile->changed = FALSE;
671 return TRUE;
675 /***********************************************************************
676 * PROFILE_ReleaseFile
678 * Flush the current profile to disk and remove it from the cache.
680 static void PROFILE_ReleaseFile(void)
682 PROFILE_FlushFile();
683 PROFILE_Free( CurProfile->section );
684 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
685 CurProfile->changed = FALSE;
686 CurProfile->section = NULL;
687 CurProfile->filename = NULL;
688 CurProfile->encoding = ENCODING_ANSI;
689 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
693 /***********************************************************************
694 * PROFILE_Open
696 * Open a profile file, checking the cached file first.
698 static BOOL PROFILE_Open( LPCWSTR filename )
700 WCHAR windirW[MAX_PATH];
701 WCHAR buffer[MAX_PATH];
702 HANDLE hFile = INVALID_HANDLE_VALUE;
703 FILETIME LastWriteTime;
704 int i,j;
705 PROFILE *tempProfile;
707 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
709 /* First time around */
711 if(!CurProfile)
712 for(i=0;i<N_CACHED_PROFILES;i++)
714 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
715 if(MRUProfile[i] == NULL) break;
716 MRUProfile[i]->changed=FALSE;
717 MRUProfile[i]->section=NULL;
718 MRUProfile[i]->filename=NULL;
719 MRUProfile[i]->encoding=ENCODING_ANSI;
720 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
723 GetWindowsDirectoryW( windirW, MAX_PATH );
725 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
726 !strchrW(filename, '\\') && !strchrW(filename, '/'))
728 static const WCHAR wszSeparator[] = {'\\', 0};
729 strcpyW(buffer, windirW);
730 strcatW(buffer, wszSeparator);
731 strcatW(buffer, filename);
733 else
735 LPWSTR dummy;
736 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
739 TRACE("path: %s\n", debugstr_w(buffer));
741 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
743 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
745 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
746 return FALSE;
749 for(i=0;i<N_CACHED_PROFILES;i++)
751 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
753 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
754 if(i)
756 PROFILE_FlushFile();
757 tempProfile=MRUProfile[i];
758 for(j=i;j>0;j--)
759 MRUProfile[j]=MRUProfile[j-1];
760 CurProfile=tempProfile;
762 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
763 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
764 TRACE("(%s): already opened (mru=%d)\n",
765 debugstr_w(buffer), i );
766 else
767 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
768 debugstr_w(buffer), i );
769 CloseHandle(hFile);
770 return TRUE;
774 /* Flush the old current profile */
775 PROFILE_FlushFile();
777 /* Make the oldest profile the current one only in order to get rid of it */
778 if(i==N_CACHED_PROFILES)
780 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
781 for(i=N_CACHED_PROFILES-1;i>0;i--)
782 MRUProfile[i]=MRUProfile[i-1];
783 CurProfile=tempProfile;
785 if(CurProfile->filename) PROFILE_ReleaseFile();
787 /* OK, now that CurProfile is definitely free we assign it our new file */
788 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
789 strcpyW( CurProfile->filename, buffer );
791 if (hFile != INVALID_HANDLE_VALUE)
793 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
794 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
795 CloseHandle(hFile);
797 else
799 /* Does not exist yet, we will create it in PROFILE_FlushFile */
800 WARN("profile file %s not found\n", debugstr_w(buffer) );
802 return TRUE;
806 /***********************************************************************
807 * PROFILE_GetSection
809 * Returns all keys of a section.
810 * If return_values is TRUE, also include the corresponding values.
812 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
813 LPWSTR buffer, UINT len, BOOL return_values )
815 PROFILEKEY *key;
817 if(!buffer) return 0;
819 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
821 while (section)
823 if (section->name[0] && !strcmpiW( section->name, section_name ))
825 UINT oldlen = len;
826 for (key = section->key; key; key = key->next)
828 if (len <= 2) break;
829 if (!*key->name) continue; /* Skip empty lines */
830 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
831 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
832 len -= strlenW(buffer) + 1;
833 buffer += strlenW(buffer) + 1;
834 if (len < 2)
835 break;
836 if (return_values && key->value) {
837 buffer[-1] = '=';
838 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
839 len -= strlenW(buffer) + 1;
840 buffer += strlenW(buffer) + 1;
843 *buffer = '\0';
844 if (len <= 1)
845 /*If either lpszSection or lpszKey is NULL and the supplied
846 destination buffer is too small to hold all the strings,
847 the last string is truncated and followed by two null characters.
848 In this case, the return value is equal to cchReturnBuffer
849 minus two. */
851 buffer[-1] = '\0';
852 return oldlen - 2;
854 return oldlen - len;
856 section = section->next;
858 buffer[0] = buffer[1] = '\0';
859 return 0;
862 /* See GetPrivateProfileSectionNamesA for documentation */
863 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
865 LPWSTR buf;
866 UINT f,l;
867 PROFILESECTION *section;
869 TRACE("(%p, %d)\n", buffer, len);
871 if (!buffer || !len)
872 return 0;
873 if (len==1) {
874 *buffer='\0';
875 return 0;
878 f=len-1;
879 buf=buffer;
880 section = CurProfile->section;
881 while ((section!=NULL)) {
882 if (section->name[0]) {
883 l = strlenW(section->name)+1;
884 if (l > f) {
885 if (f>0) {
886 strncpyW(buf, section->name, f-1);
887 buf += f-1;
888 *buf++='\0';
890 *buf='\0';
891 return len-2;
893 strcpyW(buf, section->name);
894 buf += l;
895 f -= l;
897 section = section->next;
899 *buf='\0';
900 return buf-buffer;
904 /***********************************************************************
905 * PROFILE_GetString
907 * Get a profile string.
909 * Tests with GetPrivateProfileString16, W95a,
910 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
911 * section key_name def_val res buffer
912 * "set1" "1" "x" 43 [data]
913 * "set1" "1 " "x" 43 [data] (!)
914 * "set1" " 1 "' "x" 43 [data] (!)
915 * "set1" "" "x" 1 "x"
916 * "set1" "" "x " 1 "x" (!)
917 * "set1" "" " x " 3 " x" (!)
918 * "set1" NULL "x" 6 "1\02\03\0\0"
919 * "set1" "" "x" 1 "x"
920 * NULL "1" "x" 0 "" (!)
921 * "" "1" "x" 1 "x"
922 * NULL NULL "" 0 ""
926 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
927 LPCWSTR def_val, LPWSTR buffer, UINT len )
929 PROFILEKEY *key = NULL;
930 static const WCHAR empty_strW[] = { 0 };
932 if(!buffer) return 0;
934 if (!def_val) def_val = empty_strW;
935 if (key_name)
937 if (!key_name[0])
939 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
940 return 0;
942 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
943 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
944 len, TRUE );
945 TRACE("(%s,%s,%s): returning %s\n",
946 debugstr_w(section), debugstr_w(key_name),
947 debugstr_w(def_val), debugstr_w(buffer) );
948 return strlenW( buffer );
950 /* no "else" here ! */
951 if (section && section[0])
953 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
954 if (!buffer[0]) /* no luck -> def_val */
956 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
957 ret = strlenW(buffer);
959 return ret;
961 buffer[0] = '\0';
962 return 0;
966 /***********************************************************************
967 * PROFILE_SetString
969 * Set a profile string.
971 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
972 LPCWSTR value, BOOL create_always )
974 if (!key_name) /* Delete a whole section */
976 TRACE("(%s)\n", debugstr_w(section_name));
977 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
978 section_name );
979 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
980 this is not an error on application's level.*/
982 else if (!value) /* Delete a key */
984 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
985 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
986 section_name, key_name );
987 return TRUE; /* same error handling as above */
989 else /* Set the key value */
991 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
992 key_name, TRUE, create_always );
993 TRACE("(%s,%s,%s):\n",
994 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
995 if (!key) return FALSE;
996 if (key->value)
998 /* strip the leading spaces. We can safely strip \n\r and
999 * friends too, they should not happen here anyway. */
1000 while (PROFILE_isspaceW(*value)) value++;
1002 if (!strcmpW( key->value, value ))
1004 TRACE(" no change needed\n" );
1005 return TRUE; /* No change needed */
1007 TRACE(" replacing %s\n", debugstr_w(key->value) );
1008 HeapFree( GetProcessHeap(), 0, key->value );
1010 else TRACE(" creating key\n" );
1011 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1012 strcpyW( key->value, value );
1013 CurProfile->changed = TRUE;
1015 return TRUE;
1019 /********************* API functions **********************************/
1022 /***********************************************************************
1023 * GetProfileIntA (KERNEL32.@)
1025 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1027 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1030 /***********************************************************************
1031 * GetProfileIntW (KERNEL32.@)
1033 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1035 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1039 * if allow_section_name_copy is TRUE, allow the copying :
1040 * - of Section names if 'section' is NULL
1041 * - of Keys in a Section if 'entry' is NULL
1042 * (see MSDN doc for GetPrivateProfileString)
1044 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1045 LPCWSTR def_val, LPWSTR buffer,
1046 UINT len, LPCWSTR filename,
1047 BOOL allow_section_name_copy )
1049 int ret;
1050 LPWSTR pDefVal = NULL;
1052 if (!filename)
1053 filename = wininiW;
1055 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1056 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1058 /* strip any trailing ' ' of def_val. */
1059 if (def_val)
1061 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1063 while (p > def_val)
1065 p--;
1066 if ((*p) != ' ')
1067 break;
1069 if (*p == ' ') /* ouch, contained trailing ' ' */
1071 int len = (int)(p - def_val);
1072 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1073 strncpyW(pDefVal, def_val, len);
1074 pDefVal[len] = '\0';
1077 if (!pDefVal)
1078 pDefVal = (LPWSTR)def_val;
1080 RtlEnterCriticalSection( &PROFILE_CritSect );
1082 if (PROFILE_Open( filename )) {
1083 if ((allow_section_name_copy) && (section == NULL))
1084 ret = PROFILE_GetSectionNames(buffer, len);
1085 else
1086 /* PROFILE_GetString already handles the 'entry == NULL' case */
1087 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1088 } else {
1089 lstrcpynW( buffer, pDefVal, len );
1090 ret = strlenW( buffer );
1093 RtlLeaveCriticalSection( &PROFILE_CritSect );
1095 if (pDefVal != def_val) /* allocated */
1096 HeapFree(GetProcessHeap(), 0, pDefVal);
1098 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1100 return ret;
1103 /***********************************************************************
1104 * GetPrivateProfileString (KERNEL.128)
1106 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1107 LPCSTR def_val, LPSTR buffer,
1108 UINT16 len, LPCSTR filename )
1110 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1111 LPWSTR bufferW;
1112 INT16 retW, ret = 0;
1114 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1115 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1116 else sectionW.Buffer = NULL;
1117 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1118 else entryW.Buffer = NULL;
1119 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1120 else def_valW.Buffer = NULL;
1121 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1122 else filenameW.Buffer = NULL;
1124 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1125 def_valW.Buffer, bufferW, len,
1126 filenameW.Buffer, FALSE );
1127 if (len)
1129 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1130 if (!ret)
1132 ret = len - 1;
1133 buffer[ret] = 0;
1135 else
1136 ret--; /* strip terminating 0 */
1139 RtlFreeUnicodeString(&sectionW);
1140 RtlFreeUnicodeString(&entryW);
1141 RtlFreeUnicodeString(&def_valW);
1142 RtlFreeUnicodeString(&filenameW);
1143 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1144 return ret;
1147 /***********************************************************************
1148 * GetPrivateProfileStringA (KERNEL32.@)
1150 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1151 LPCSTR def_val, LPSTR buffer,
1152 UINT len, LPCSTR filename )
1154 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1155 LPWSTR bufferW;
1156 INT retW, ret = 0;
1158 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1159 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1160 else sectionW.Buffer = NULL;
1161 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1162 else entryW.Buffer = NULL;
1163 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1164 else def_valW.Buffer = NULL;
1165 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1166 else filenameW.Buffer = NULL;
1168 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1169 def_valW.Buffer, bufferW, len,
1170 filenameW.Buffer);
1171 if (len)
1173 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1174 if (!ret)
1176 ret = len - 1;
1177 buffer[ret] = 0;
1179 else
1180 ret--; /* strip terminating 0 */
1183 RtlFreeUnicodeString(&sectionW);
1184 RtlFreeUnicodeString(&entryW);
1185 RtlFreeUnicodeString(&def_valW);
1186 RtlFreeUnicodeString(&filenameW);
1187 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1188 return ret;
1191 /***********************************************************************
1192 * GetPrivateProfileStringW (KERNEL32.@)
1194 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1195 LPCWSTR def_val, LPWSTR buffer,
1196 UINT len, LPCWSTR filename )
1198 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1200 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1201 buffer, len, filename, TRUE );
1204 /***********************************************************************
1205 * GetProfileStringA (KERNEL32.@)
1207 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1208 LPSTR buffer, UINT len )
1210 return GetPrivateProfileStringA( section, entry, def_val,
1211 buffer, len, "win.ini" );
1214 /***********************************************************************
1215 * GetProfileStringW (KERNEL32.@)
1217 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1218 LPCWSTR def_val, LPWSTR buffer, UINT len )
1220 return GetPrivateProfileStringW( section, entry, def_val,
1221 buffer, len, wininiW );
1224 /***********************************************************************
1225 * WriteProfileStringA (KERNEL32.@)
1227 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1228 LPCSTR string )
1230 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1233 /***********************************************************************
1234 * WriteProfileStringW (KERNEL32.@)
1236 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1237 LPCWSTR string )
1239 return WritePrivateProfileStringW( section, entry, string, wininiW );
1243 /***********************************************************************
1244 * GetPrivateProfileIntW (KERNEL32.@)
1246 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1247 INT def_val, LPCWSTR filename )
1249 WCHAR buffer[30];
1250 UNICODE_STRING bufferW;
1251 INT len;
1252 ULONG result;
1254 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1255 buffer, sizeof(buffer)/sizeof(WCHAR),
1256 filename )))
1257 return def_val;
1259 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1261 /* FIXME: if entry can be found but it's empty, then Win16 is
1262 * supposed to return 0 instead of def_val ! Difficult/problematic
1263 * to implement (every other failure also returns zero buffer),
1264 * thus wait until testing framework avail for making sure nothing
1265 * else gets broken that way. */
1266 if (!buffer[0]) return (UINT)def_val;
1268 RtlInitUnicodeString( &bufferW, buffer );
1269 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1270 return result;
1273 /***********************************************************************
1274 * GetPrivateProfileIntA (KERNEL32.@)
1276 * FIXME: rewrite using unicode
1278 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1279 INT def_val, LPCSTR filename )
1281 UNICODE_STRING entryW, filenameW, sectionW;
1282 UINT res;
1283 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1284 else entryW.Buffer = NULL;
1285 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1286 else filenameW.Buffer = NULL;
1287 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1288 else sectionW.Buffer = NULL;
1289 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1290 filenameW.Buffer);
1291 RtlFreeUnicodeString(&sectionW);
1292 RtlFreeUnicodeString(&filenameW);
1293 RtlFreeUnicodeString(&entryW);
1294 return res;
1297 /***********************************************************************
1298 * GetPrivateProfileSectionW (KERNEL32.@)
1300 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1301 DWORD len, LPCWSTR filename )
1303 int ret = 0;
1305 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1307 RtlEnterCriticalSection( &PROFILE_CritSect );
1309 if (PROFILE_Open( filename ))
1310 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1312 RtlLeaveCriticalSection( &PROFILE_CritSect );
1314 return ret;
1317 /***********************************************************************
1318 * GetPrivateProfileSectionA (KERNEL32.@)
1320 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1321 DWORD len, LPCSTR filename )
1323 UNICODE_STRING sectionW, filenameW;
1324 LPWSTR bufferW;
1325 INT retW, ret = 0;
1327 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1328 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1329 else sectionW.Buffer = NULL;
1330 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1331 else filenameW.Buffer = NULL;
1333 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1334 if (len > 2)
1336 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1337 if (ret > 2)
1338 ret -= 2;
1339 else
1341 ret = 0;
1342 buffer[len-2] = 0;
1343 buffer[len-1] = 0;
1346 else
1348 buffer[0] = 0;
1349 buffer[1] = 0;
1352 RtlFreeUnicodeString(&sectionW);
1353 RtlFreeUnicodeString(&filenameW);
1354 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1355 return ret;
1358 /***********************************************************************
1359 * GetProfileSectionA (KERNEL32.@)
1361 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1363 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1366 /***********************************************************************
1367 * GetProfileSectionW (KERNEL32.@)
1369 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1371 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1375 /***********************************************************************
1376 * WritePrivateProfileStringW (KERNEL32.@)
1378 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1379 LPCWSTR string, LPCWSTR filename )
1381 BOOL ret = FALSE;
1383 RtlEnterCriticalSection( &PROFILE_CritSect );
1385 if (PROFILE_Open( filename ))
1387 if (!section && !entry && !string) /* documented "file flush" case */
1389 PROFILE_FlushFile();
1390 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1392 else {
1393 if (!section) {
1394 FIXME("(NULL?,%s,%s,%s)?\n",
1395 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1396 } else {
1397 ret = PROFILE_SetString( section, entry, string, FALSE);
1398 PROFILE_FlushFile();
1403 RtlLeaveCriticalSection( &PROFILE_CritSect );
1404 return ret;
1407 /***********************************************************************
1408 * WritePrivateProfileStringA (KERNEL32.@)
1410 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1411 LPCSTR string, LPCSTR filename )
1413 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1414 BOOL ret;
1416 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1417 else sectionW.Buffer = NULL;
1418 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1419 else entryW.Buffer = NULL;
1420 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1421 else stringW.Buffer = NULL;
1422 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1423 else filenameW.Buffer = NULL;
1425 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1426 stringW.Buffer, filenameW.Buffer);
1427 RtlFreeUnicodeString(&sectionW);
1428 RtlFreeUnicodeString(&entryW);
1429 RtlFreeUnicodeString(&stringW);
1430 RtlFreeUnicodeString(&filenameW);
1431 return ret;
1434 /***********************************************************************
1435 * WritePrivateProfileSectionW (KERNEL32.@)
1437 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1438 LPCWSTR string, LPCWSTR filename )
1440 BOOL ret = FALSE;
1441 LPWSTR p;
1443 RtlEnterCriticalSection( &PROFILE_CritSect );
1445 if (PROFILE_Open( filename )) {
1446 if (!section && !string)
1447 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1448 else if (!string) {/* delete the named section*/
1449 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1450 PROFILE_FlushFile();
1451 } else {
1452 PROFILE_DeleteAllKeys(section);
1453 ret = TRUE;
1454 while(*string) {
1455 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1456 strcpyW( buf, string );
1457 if((p = strchrW( buf, '='))) {
1458 *p='\0';
1459 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1461 HeapFree( GetProcessHeap(), 0, buf );
1462 string += strlenW(string)+1;
1464 PROFILE_FlushFile();
1468 RtlLeaveCriticalSection( &PROFILE_CritSect );
1469 return ret;
1472 /***********************************************************************
1473 * WritePrivateProfileSectionA (KERNEL32.@)
1475 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1476 LPCSTR string, LPCSTR filename)
1479 UNICODE_STRING sectionW, filenameW;
1480 LPWSTR stringW;
1481 BOOL ret;
1483 if (string)
1485 INT lenA, lenW;
1486 LPCSTR p = string;
1488 while(*p) p += strlen(p) + 1;
1489 lenA = p - string + 1;
1490 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1491 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1492 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1494 else stringW = NULL;
1495 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1496 else sectionW.Buffer = NULL;
1497 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1498 else filenameW.Buffer = NULL;
1500 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1502 HeapFree(GetProcessHeap(), 0, stringW);
1503 RtlFreeUnicodeString(&sectionW);
1504 RtlFreeUnicodeString(&filenameW);
1505 return ret;
1508 /***********************************************************************
1509 * WriteProfileSectionA (KERNEL32.@)
1511 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1514 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1517 /***********************************************************************
1518 * WriteProfileSectionW (KERNEL32.@)
1520 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1522 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1526 /***********************************************************************
1527 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1529 * Returns the section names contained in the specified file.
1530 * FIXME: Where do we find this file when the path is relative?
1531 * The section names are returned as a list of strings with an extra
1532 * '\0' to mark the end of the list. Except for that the behavior
1533 * depends on the Windows version.
1535 * Win95:
1536 * - if the buffer is 0 or 1 character long then it is as if it was of
1537 * infinite length.
1538 * - otherwise, if the buffer is to small only the section names that fit
1539 * are returned.
1540 * - note that this means if the buffer was to small to return even just
1541 * the first section name then a single '\0' will be returned.
1542 * - the return value is the number of characters written in the buffer,
1543 * except if the buffer was too smal in which case len-2 is returned
1545 * Win2000:
1546 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1547 * '\0' and the return value is 0
1548 * - otherwise if the buffer is too small then the first section name that
1549 * does not fit is truncated so that the string list can be terminated
1550 * correctly (double '\0')
1551 * - the return value is the number of characters written in the buffer
1552 * except for the trailing '\0'. If the buffer is too small, then the
1553 * return value is len-2
1554 * - Win2000 has a bug that triggers when the section names and the
1555 * trailing '\0' fit exactly in the buffer. In that case the trailing
1556 * '\0' is missing.
1558 * Wine implements the observed Win2000 behavior (except for the bug).
1560 * Note that when the buffer is big enough then the return value may be any
1561 * value between 1 and len-1 (or len in Win95), including len-2.
1563 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1564 LPCWSTR filename)
1566 DWORD ret = 0;
1568 RtlEnterCriticalSection( &PROFILE_CritSect );
1570 if (PROFILE_Open( filename ))
1571 ret = PROFILE_GetSectionNames(buffer, size);
1573 RtlLeaveCriticalSection( &PROFILE_CritSect );
1575 return ret;
1579 /***********************************************************************
1580 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1582 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1583 LPCSTR filename)
1585 UNICODE_STRING filenameW;
1586 LPWSTR bufferW;
1587 INT retW, ret = 0;
1589 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1590 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1591 else filenameW.Buffer = NULL;
1593 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1594 if (retW && size)
1596 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1597 if (!ret)
1599 ret = size;
1600 buffer[size-1] = 0;
1604 RtlFreeUnicodeString(&filenameW);
1605 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1606 return ret;
1609 /***********************************************************************
1610 * GetPrivateProfileStructW (KERNEL32.@)
1612 * Should match Win95's behaviour pretty much
1614 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1615 LPVOID buf, UINT len, LPCWSTR filename)
1617 BOOL ret = FALSE;
1619 RtlEnterCriticalSection( &PROFILE_CritSect );
1621 if (PROFILE_Open( filename )) {
1622 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1623 if (k) {
1624 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1625 if (((strlenW(k->value) - 2) / 2) == len)
1627 LPWSTR end, p;
1628 BOOL valid = TRUE;
1629 WCHAR c;
1630 DWORD chksum = 0;
1632 end = k->value + strlenW(k->value); /* -> '\0' */
1633 /* check for invalid chars in ASCII coded hex string */
1634 for (p=k->value; p < end; p++)
1636 if (!isxdigitW(*p))
1638 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1639 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1640 valid = FALSE;
1641 break;
1644 if (valid)
1646 BOOL highnibble = TRUE;
1647 BYTE b = 0, val;
1648 LPBYTE binbuf = (LPBYTE)buf;
1650 end -= 2; /* don't include checksum in output data */
1651 /* translate ASCII hex format into binary data */
1652 for (p=k->value; p < end; p++)
1654 c = toupperW(*p);
1655 val = (c > '9') ?
1656 (c - 'A' + 10) : (c - '0');
1658 if (highnibble)
1659 b = val << 4;
1660 else
1662 b += val;
1663 *binbuf++ = b; /* feed binary data into output */
1664 chksum += b; /* calculate checksum */
1666 highnibble ^= 1; /* toggle */
1668 /* retrieve stored checksum value */
1669 c = toupperW(*p++);
1670 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1671 c = toupperW(*p);
1672 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1673 if (b == (chksum & 0xff)) /* checksums match ? */
1674 ret = TRUE;
1679 RtlLeaveCriticalSection( &PROFILE_CritSect );
1681 return ret;
1684 /***********************************************************************
1685 * GetPrivateProfileStructA (KERNEL32.@)
1687 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1688 LPVOID buffer, UINT len, LPCSTR filename)
1690 UNICODE_STRING sectionW, keyW, filenameW;
1691 INT ret;
1693 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1694 else sectionW.Buffer = NULL;
1695 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1696 else keyW.Buffer = NULL;
1697 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1698 else filenameW.Buffer = NULL;
1700 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1701 filenameW.Buffer);
1702 /* Do not translate binary data. */
1704 RtlFreeUnicodeString(&sectionW);
1705 RtlFreeUnicodeString(&keyW);
1706 RtlFreeUnicodeString(&filenameW);
1707 return ret;
1712 /***********************************************************************
1713 * WritePrivateProfileStructW (KERNEL32.@)
1715 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1716 LPVOID buf, UINT bufsize, LPCWSTR filename)
1718 BOOL ret = FALSE;
1719 LPBYTE binbuf;
1720 LPWSTR outstring, p;
1721 DWORD sum = 0;
1723 if (!section && !key && !buf) /* flush the cache */
1724 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1726 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1727 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1728 p = outstring;
1729 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1730 *p++ = hex[*binbuf >> 4];
1731 *p++ = hex[*binbuf & 0xf];
1732 sum += *binbuf;
1734 /* checksum is sum & 0xff */
1735 *p++ = hex[(sum & 0xf0) >> 4];
1736 *p++ = hex[sum & 0xf];
1737 *p++ = '\0';
1739 RtlEnterCriticalSection( &PROFILE_CritSect );
1741 if (PROFILE_Open( filename )) {
1742 ret = PROFILE_SetString( section, key, outstring, FALSE);
1743 PROFILE_FlushFile();
1746 RtlLeaveCriticalSection( &PROFILE_CritSect );
1748 HeapFree( GetProcessHeap(), 0, outstring );
1750 return ret;
1753 /***********************************************************************
1754 * WritePrivateProfileStructA (KERNEL32.@)
1756 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1757 LPVOID buf, UINT bufsize, LPCSTR filename)
1759 UNICODE_STRING sectionW, keyW, filenameW;
1760 INT ret;
1762 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1763 else sectionW.Buffer = NULL;
1764 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1765 else keyW.Buffer = NULL;
1766 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1767 else filenameW.Buffer = NULL;
1769 /* Do not translate binary data. */
1770 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1771 filenameW.Buffer);
1773 RtlFreeUnicodeString(&sectionW);
1774 RtlFreeUnicodeString(&keyW);
1775 RtlFreeUnicodeString(&filenameW);
1776 return ret;
1780 /***********************************************************************
1781 * WriteOutProfiles (KERNEL.315)
1783 void WINAPI WriteOutProfiles16(void)
1785 RtlEnterCriticalSection( &PROFILE_CritSect );
1786 PROFILE_FlushFile();
1787 RtlLeaveCriticalSection( &PROFILE_CritSect );
1790 /***********************************************************************
1791 * CloseProfileUserMapping (KERNEL32.@)
1793 BOOL WINAPI CloseProfileUserMapping(void) {
1794 FIXME("(), stub!\n");
1795 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1796 return FALSE;