Fixed white space handling on the last line (reported by Jeremy
[wine/multimedia.git] / dlls / kernel / profile.c
blob0022134db6777521ba781e7eefede806fcb7aebb
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 "winternl.h"
33 #include "wine/winbase16.h"
34 #include "wine/unicode.h"
35 #include "wine/server.h"
36 #include "wine/library.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(profile);
41 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
43 typedef enum
45 ENCODING_ANSI = 1,
46 ENCODING_UTF8,
47 ENCODING_UTF16LE,
48 ENCODING_UTF16BE
49 } ENCODING;
51 typedef struct tagPROFILEKEY
53 WCHAR *value;
54 struct tagPROFILEKEY *next;
55 WCHAR name[1];
56 } PROFILEKEY;
58 typedef struct tagPROFILESECTION
60 struct tagPROFILEKEY *key;
61 struct tagPROFILESECTION *next;
62 WCHAR name[1];
63 } PROFILESECTION;
66 typedef struct
68 BOOL changed;
69 PROFILESECTION *section;
70 WCHAR *filename;
71 FILETIME LastWriteTime;
72 ENCODING encoding;
73 } PROFILE;
76 #define N_CACHED_PROFILES 10
78 /* Cached profile files */
79 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
81 #define CurProfile (MRUProfile[0])
83 /* Check for comments in profile */
84 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
86 static const WCHAR emptystringW[] = {0};
87 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
89 static CRITICAL_SECTION PROFILE_CritSect;
90 static CRITICAL_SECTION_DEBUG critsect_debug =
92 0, 0, &PROFILE_CritSect,
93 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
94 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
96 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
98 static const char hex[16] = "0123456789ABCDEF";
100 /***********************************************************************
101 * PROFILE_CopyEntry
103 * Copy the content of an entry into a buffer, removing quotes, and possibly
104 * translating environment variables.
106 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
107 BOOL strip_quote )
109 WCHAR quote = '\0';
111 if(!buffer) return;
113 if (strip_quote && ((*value == '\'') || (*value == '\"')))
115 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
118 lstrcpynW( buffer, value, len );
119 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
122 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
123 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
125 int i;
126 USHORT * shortbuffer = (USHORT *)buffer;
127 for (i = 0; i < len; i++)
128 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
131 /* writes any necessary encoding marker to the file */
132 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
134 DWORD dwBytesWritten;
135 DWORD bom;
136 switch (encoding)
138 case ENCODING_ANSI:
139 break;
140 case ENCODING_UTF8:
141 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
142 break;
143 case ENCODING_UTF16LE:
144 bom = 0xFEFF;
145 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
146 break;
147 case ENCODING_UTF16BE:
148 bom = 0xFFFE;
149 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
150 break;
154 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
156 char * write_buffer;
157 int write_buffer_len;
158 DWORD dwBytesWritten;
160 TRACE("writing: %s\n", debugstr_wn(szLine, len));
162 switch (encoding)
164 case ENCODING_ANSI:
165 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
166 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
167 if (!write_buffer) return;
168 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
169 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
170 HeapFree(GetProcessHeap(), 0, write_buffer);
171 break;
172 case ENCODING_UTF8:
173 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
174 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
175 if (!write_buffer) return;
176 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
177 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
178 HeapFree(GetProcessHeap(), 0, write_buffer);
179 break;
180 case ENCODING_UTF16LE:
181 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
182 break;
183 case ENCODING_UTF16BE:
184 PROFILE_ByteSwapShortBuffer(szLine, len);
185 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
186 break;
187 default:
188 FIXME("encoding type %d not implemented\n", encoding);
192 /***********************************************************************
193 * PROFILE_Save
195 * Save a profile tree to a file.
197 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
199 PROFILEKEY *key;
200 WCHAR *buffer, *p;
202 PROFILE_WriteMarker(hFile, encoding);
204 for ( ; section; section = section->next)
206 int len = 0;
208 if (section->name[0]) len += strlenW(section->name) + 6;
210 for (key = section->key; key; key = key->next)
212 len += strlenW(key->name) + 2;
213 if (key->value) len += strlenW(key->value) + 1;
216 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
217 if (!buffer) return;
219 p = buffer;
220 if (section->name[0])
222 *p++ = '\r';
223 *p++ = '\n';
224 *p++ = '[';
225 strcpyW( p, section->name );
226 p += strlenW(p);
227 *p++ = ']';
228 *p++ = '\r';
229 *p++ = '\n';
231 for (key = section->key; key; key = key->next)
233 strcpyW( p, key->name );
234 p += strlenW(p);
235 if (key->value)
237 *p++ = '=';
238 strcpyW( p, key->value );
239 p += strlenW(p);
241 *p++ = '\r';
242 *p++ = '\n';
244 PROFILE_WriteLine( hFile, buffer, len, encoding );
245 HeapFree(GetProcessHeap(), 0, buffer);
250 /***********************************************************************
251 * PROFILE_Free
253 * Free a profile tree.
255 static void PROFILE_Free( PROFILESECTION *section )
257 PROFILESECTION *next_section;
258 PROFILEKEY *key, *next_key;
260 for ( ; section; section = next_section)
262 for (key = section->key; key; key = next_key)
264 next_key = key->next;
265 HeapFree( GetProcessHeap(), 0, key->value );
266 HeapFree( GetProcessHeap(), 0, key );
268 next_section = section->next;
269 HeapFree( GetProcessHeap(), 0, section );
273 /* returns 1 if a character white space else 0 */
274 static inline int PROFILE_isspaceW(WCHAR c)
276 if (isspaceW(c)) return 1;
277 if (c=='\r' || c==0x1a) return 1;
278 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
279 return 0;
282 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
284 int flags = IS_TEXT_UNICODE_SIGNATURE |
285 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
286 IS_TEXT_UNICODE_ODD_LENGTH;
287 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
289 *len = sizeof(bom_utf8);
290 return ENCODING_UTF8;
292 RtlIsTextUnicode(buffer, *len, &flags);
293 if (flags & IS_TEXT_UNICODE_SIGNATURE)
295 *len = sizeof(WCHAR);
296 return ENCODING_UTF16LE;
298 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
300 *len = sizeof(WCHAR);
301 return ENCODING_UTF16BE;
303 *len = 0;
304 return ENCODING_ANSI;
308 /***********************************************************************
309 * PROFILE_Load
311 * Load a profile tree from a file.
313 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
315 void *pBuffer;
316 WCHAR * szFile;
317 const WCHAR *szLineStart, *szLineEnd;
318 const WCHAR *szValueStart, *szEnd, *next_line;
319 int line = 0, len;
320 PROFILESECTION *section, *first_section;
321 PROFILESECTION **next_section;
322 PROFILEKEY *key, *prev_key, **next_key;
323 DWORD dwFileSize;
325 TRACE("%p\n", hFile);
327 dwFileSize = GetFileSize(hFile, NULL);
328 if (dwFileSize == INVALID_FILE_SIZE)
329 return NULL;
331 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
332 if (!pBuffer) return NULL;
334 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
336 HeapFree(GetProcessHeap(), 0, pBuffer);
337 WARN("Error %ld reading file\n", GetLastError());
338 return NULL;
340 len = dwFileSize;
341 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
342 /* len is set to the number of bytes in the character marker.
343 * we want to skip these bytes */
344 pBuffer = (char *)pBuffer + len;
345 dwFileSize -= len;
346 switch (*pEncoding)
348 case ENCODING_ANSI:
349 TRACE("ANSI encoding\n");
351 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
352 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
353 if (!szFile)
355 HeapFree(GetProcessHeap(), 0, pBuffer);
356 return NULL;
358 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
359 szEnd = szFile + len;
360 break;
361 case ENCODING_UTF8:
362 TRACE("UTF8 encoding\n");
364 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
365 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
366 if (!szFile)
368 HeapFree(GetProcessHeap(), 0, pBuffer);
369 return NULL;
371 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
372 szEnd = szFile + len;
373 break;
374 case ENCODING_UTF16LE:
375 TRACE("UTF16 Little Endian encoding\n");
376 szFile = (WCHAR *)pBuffer + 1;
377 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
378 break;
379 case ENCODING_UTF16BE:
380 TRACE("UTF16 Big Endian encoding\n");
381 szFile = (WCHAR *)pBuffer + 1;
382 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
383 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
384 break;
385 default:
386 FIXME("encoding type %d not implemented\n", *pEncoding);
387 HeapFree(GetProcessHeap(), 0, pBuffer);
388 return NULL;
391 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
392 if(first_section == NULL)
394 if (szFile != pBuffer)
395 HeapFree(GetProcessHeap(), 0, szFile);
396 HeapFree(GetProcessHeap(), 0, pBuffer);
397 return NULL;
399 first_section->name[0] = 0;
400 first_section->key = NULL;
401 first_section->next = NULL;
402 next_section = &first_section->next;
403 next_key = &first_section->key;
404 prev_key = NULL;
405 next_line = szFile;
407 while (next_line < szEnd)
409 szLineStart = next_line;
410 next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
411 if (!next_line) next_line = szEnd;
412 else next_line++;
413 szLineEnd = next_line;
415 line++;
417 /* get rid of white space */
418 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
419 while ((szLineEnd > szLineStart) && ((szLineEnd[-1] == '\n') || PROFILE_isspaceW(szLineEnd[-1]))) szLineEnd--;
421 if (szLineStart >= szLineEnd) continue;
423 if (*szLineStart == '[') /* section start */
425 const WCHAR * szSectionEnd;
426 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
428 WARN("Invalid section header at line %d: %s\n",
429 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
431 else
433 szLineStart++;
434 len = (int)(szSectionEnd - szLineStart);
435 /* no need to allocate +1 for NULL terminating character as
436 * already included in structure */
437 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
438 break;
439 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
440 section->name[len] = '\0';
441 section->key = NULL;
442 section->next = NULL;
443 *next_section = section;
444 next_section = &section->next;
445 next_key = &section->key;
446 prev_key = NULL;
448 TRACE("New section: %s\n", debugstr_w(section->name));
450 continue;
454 /* get rid of white space after the name and before the start
455 * of the value */
456 len = szLineEnd - szLineStart;
457 if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
459 const WCHAR *szNameEnd = szValueStart;
460 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
461 len = szNameEnd - szLineStart;
462 szValueStart++;
463 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
466 if (len || !prev_key || *prev_key->name)
468 /* no need to allocate +1 for NULL terminating character as
469 * already included in structure */
470 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
471 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
472 key->name[len] = '\0';
473 if (szValueStart)
475 len = (int)(szLineEnd - szValueStart);
476 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
477 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
478 key->value[len] = '\0';
480 else key->value = NULL;
482 key->next = NULL;
483 *next_key = key;
484 next_key = &key->next;
485 prev_key = key;
487 TRACE("New key: name=%s, value=%s\n",
488 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
491 if (szFile != pBuffer)
492 HeapFree(GetProcessHeap(), 0, szFile);
493 HeapFree(GetProcessHeap(), 0, pBuffer);
494 return first_section;
498 /***********************************************************************
499 * PROFILE_DeleteSection
501 * Delete a section from a profile tree.
503 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
505 while (*section)
507 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
509 PROFILESECTION *to_del = *section;
510 *section = to_del->next;
511 to_del->next = NULL;
512 PROFILE_Free( to_del );
513 return TRUE;
515 section = &(*section)->next;
517 return FALSE;
521 /***********************************************************************
522 * PROFILE_DeleteKey
524 * Delete a key from a profile tree.
526 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
527 LPCWSTR section_name, LPCWSTR key_name )
529 while (*section)
531 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
533 PROFILEKEY **key = &(*section)->key;
534 while (*key)
536 if (!strcmpiW( (*key)->name, key_name ))
538 PROFILEKEY *to_del = *key;
539 *key = to_del->next;
540 HeapFree( GetProcessHeap(), 0, to_del->value);
541 HeapFree( GetProcessHeap(), 0, to_del );
542 return TRUE;
544 key = &(*key)->next;
547 section = &(*section)->next;
549 return FALSE;
553 /***********************************************************************
554 * PROFILE_DeleteAllKeys
556 * Delete all keys from a profile tree.
558 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
560 PROFILESECTION **section= &CurProfile->section;
561 while (*section)
563 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
565 PROFILEKEY **key = &(*section)->key;
566 while (*key)
568 PROFILEKEY *to_del = *key;
569 *key = to_del->next;
570 HeapFree( GetProcessHeap(), 0, to_del->value);
571 HeapFree( GetProcessHeap(), 0, to_del );
572 CurProfile->changed =TRUE;
575 section = &(*section)->next;
580 /***********************************************************************
581 * PROFILE_Find
583 * Find a key in a profile tree, optionally creating it.
585 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
586 LPCWSTR key_name, BOOL create, BOOL create_always )
588 LPCWSTR p;
589 int seclen, keylen;
591 while (PROFILE_isspaceW(*section_name)) section_name++;
592 p = section_name + strlenW(section_name) - 1;
593 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
594 seclen = p - section_name + 1;
596 while (PROFILE_isspaceW(*key_name)) key_name++;
597 p = key_name + strlenW(key_name) - 1;
598 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
599 keylen = p - key_name + 1;
601 while (*section)
603 if ( ((*section)->name[0])
604 && (!(strncmpiW( (*section)->name, section_name, seclen )))
605 && (((*section)->name)[seclen] == '\0') )
607 PROFILEKEY **key = &(*section)->key;
609 while (*key)
611 /* If create_always is FALSE then we check if the keyname
612 * already exists. Otherwise we add it regardless of its
613 * existence, to allow keys to be added more than once in
614 * some cases.
616 if(!create_always)
618 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
619 && (((*key)->name)[keylen] == '\0') )
620 return *key;
622 key = &(*key)->next;
624 if (!create) return NULL;
625 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
626 return NULL;
627 strcpyW( (*key)->name, key_name );
628 (*key)->value = NULL;
629 (*key)->next = NULL;
630 return *key;
632 section = &(*section)->next;
634 if (!create) return NULL;
635 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
636 if(*section == NULL) return NULL;
637 strcpyW( (*section)->name, section_name );
638 (*section)->next = NULL;
639 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
640 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
642 HeapFree(GetProcessHeap(), 0, *section);
643 return NULL;
645 strcpyW( (*section)->key->name, key_name );
646 (*section)->key->value = NULL;
647 (*section)->key->next = NULL;
648 return (*section)->key;
652 /***********************************************************************
653 * PROFILE_FlushFile
655 * Flush the current profile to disk if changed.
657 static BOOL PROFILE_FlushFile(void)
659 HANDLE hFile = NULL;
660 FILETIME LastWriteTime;
662 if(!CurProfile)
664 WARN("No current profile!\n");
665 return FALSE;
668 if (!CurProfile->changed) return TRUE;
670 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
672 if (hFile == INVALID_HANDLE_VALUE)
674 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
675 return FALSE;
678 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
679 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
680 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
681 CurProfile->LastWriteTime=LastWriteTime;
682 CloseHandle( hFile );
683 CurProfile->changed = FALSE;
684 return TRUE;
688 /***********************************************************************
689 * PROFILE_ReleaseFile
691 * Flush the current profile to disk and remove it from the cache.
693 static void PROFILE_ReleaseFile(void)
695 PROFILE_FlushFile();
696 PROFILE_Free( CurProfile->section );
697 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
698 CurProfile->changed = FALSE;
699 CurProfile->section = NULL;
700 CurProfile->filename = NULL;
701 CurProfile->encoding = ENCODING_ANSI;
702 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
706 /***********************************************************************
707 * PROFILE_Open
709 * Open a profile file, checking the cached file first.
711 static BOOL PROFILE_Open( LPCWSTR filename )
713 WCHAR windirW[MAX_PATH];
714 WCHAR buffer[MAX_PATH];
715 HANDLE hFile = INVALID_HANDLE_VALUE;
716 FILETIME LastWriteTime;
717 int i,j;
718 PROFILE *tempProfile;
720 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
722 /* First time around */
724 if(!CurProfile)
725 for(i=0;i<N_CACHED_PROFILES;i++)
727 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
728 if(MRUProfile[i] == NULL) break;
729 MRUProfile[i]->changed=FALSE;
730 MRUProfile[i]->section=NULL;
731 MRUProfile[i]->filename=NULL;
732 MRUProfile[i]->encoding=ENCODING_ANSI;
733 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
736 GetWindowsDirectoryW( windirW, MAX_PATH );
738 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
739 !strchrW(filename, '\\') && !strchrW(filename, '/'))
741 static const WCHAR wszSeparator[] = {'\\', 0};
742 strcpyW(buffer, windirW);
743 strcatW(buffer, wszSeparator);
744 strcatW(buffer, filename);
746 else
748 LPWSTR dummy;
749 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
752 TRACE("path: %s\n", debugstr_w(buffer));
754 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
756 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
758 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
759 return FALSE;
762 for(i=0;i<N_CACHED_PROFILES;i++)
764 if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
766 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
767 if(i)
769 PROFILE_FlushFile();
770 tempProfile=MRUProfile[i];
771 for(j=i;j>0;j--)
772 MRUProfile[j]=MRUProfile[j-1];
773 CurProfile=tempProfile;
776 if (hFile != INVALID_HANDLE_VALUE)
778 if (TRACE_ON(profile))
780 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
781 if (memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
782 TRACE("(%s): already opened (mru=%d)\n",
783 debugstr_w(buffer), i);
784 else
785 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
786 debugstr_w(buffer), i);
788 CloseHandle(hFile);
790 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
791 debugstr_w(buffer), i);
792 return TRUE;
796 /* Flush the old current profile */
797 PROFILE_FlushFile();
799 /* Make the oldest profile the current one only in order to get rid of it */
800 if(i==N_CACHED_PROFILES)
802 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
803 for(i=N_CACHED_PROFILES-1;i>0;i--)
804 MRUProfile[i]=MRUProfile[i-1];
805 CurProfile=tempProfile;
807 if(CurProfile->filename) PROFILE_ReleaseFile();
809 /* OK, now that CurProfile is definitely free we assign it our new file */
810 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
811 strcpyW( CurProfile->filename, buffer );
813 if (hFile != INVALID_HANDLE_VALUE)
815 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
816 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
817 CloseHandle(hFile);
819 else
821 /* Does not exist yet, we will create it in PROFILE_FlushFile */
822 WARN("profile file %s not found\n", debugstr_w(buffer) );
824 return TRUE;
828 /***********************************************************************
829 * PROFILE_GetSection
831 * Returns all keys of a section.
832 * If return_values is TRUE, also include the corresponding values.
834 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
835 LPWSTR buffer, UINT len, BOOL return_values )
837 PROFILEKEY *key;
839 if(!buffer) return 0;
841 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
843 while (section)
845 if (section->name[0] && !strcmpiW( section->name, section_name ))
847 UINT oldlen = len;
848 for (key = section->key; key; key = key->next)
850 if (len <= 2) break;
851 if (!*key->name) continue; /* Skip empty lines */
852 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
853 if (!return_values && !key->value) continue; /* Skip lines w.o. '=' */
854 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
855 len -= strlenW(buffer) + 1;
856 buffer += strlenW(buffer) + 1;
857 if (len < 2)
858 break;
859 if (return_values && key->value) {
860 buffer[-1] = '=';
861 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
862 len -= strlenW(buffer) + 1;
863 buffer += strlenW(buffer) + 1;
866 *buffer = '\0';
867 if (len <= 1)
868 /*If either lpszSection or lpszKey is NULL and the supplied
869 destination buffer is too small to hold all the strings,
870 the last string is truncated and followed by two null characters.
871 In this case, the return value is equal to cchReturnBuffer
872 minus two. */
874 buffer[-1] = '\0';
875 return oldlen - 2;
877 return oldlen - len;
879 section = section->next;
881 buffer[0] = buffer[1] = '\0';
882 return 0;
885 /* See GetPrivateProfileSectionNamesA for documentation */
886 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
888 LPWSTR buf;
889 UINT buflen,tmplen;
890 PROFILESECTION *section;
892 TRACE("(%p, %d)\n", buffer, len);
894 if (!buffer || !len)
895 return 0;
896 if (len==1) {
897 *buffer='\0';
898 return 0;
901 buflen=len-1;
902 buf=buffer;
903 section = CurProfile->section;
904 while ((section!=NULL)) {
905 if (section->name[0]) {
906 tmplen = strlenW(section->name)+1;
907 if (tmplen > buflen) {
908 if (buflen > 0) {
909 memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
910 buf += buflen-1;
911 *buf++='\0';
913 *buf='\0';
914 return len-2;
916 memcpy(buf, section->name, tmplen * sizeof(WCHAR));
917 buf += tmplen;
918 buflen -= tmplen;
920 section = section->next;
922 *buf='\0';
923 return buf-buffer;
927 /***********************************************************************
928 * PROFILE_GetString
930 * Get a profile string.
932 * Tests with GetPrivateProfileString16, W95a,
933 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
934 * section key_name def_val res buffer
935 * "set1" "1" "x" 43 [data]
936 * "set1" "1 " "x" 43 [data] (!)
937 * "set1" " 1 "' "x" 43 [data] (!)
938 * "set1" "" "x" 1 "x"
939 * "set1" "" "x " 1 "x" (!)
940 * "set1" "" " x " 3 " x" (!)
941 * "set1" NULL "x" 6 "1\02\03\0\0"
942 * "set1" "" "x" 1 "x"
943 * NULL "1" "x" 0 "" (!)
944 * "" "1" "x" 1 "x"
945 * NULL NULL "" 0 ""
949 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
950 LPCWSTR def_val, LPWSTR buffer, UINT len )
952 PROFILEKEY *key = NULL;
953 static const WCHAR empty_strW[] = { 0 };
955 if(!buffer) return 0;
957 if (!def_val) def_val = empty_strW;
958 if (key_name)
960 if (!key_name[0])
962 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
963 return 0;
965 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
966 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
967 len, TRUE );
968 TRACE("(%s,%s,%s): returning %s\n",
969 debugstr_w(section), debugstr_w(key_name),
970 debugstr_w(def_val), debugstr_w(buffer) );
971 return strlenW( buffer );
973 /* no "else" here ! */
974 if (section && section[0])
976 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
977 if (!buffer[0]) /* no luck -> def_val */
979 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
980 ret = strlenW(buffer);
982 return ret;
984 buffer[0] = '\0';
985 return 0;
989 /***********************************************************************
990 * PROFILE_SetString
992 * Set a profile string.
994 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
995 LPCWSTR value, BOOL create_always )
997 if (!key_name) /* Delete a whole section */
999 TRACE("(%s)\n", debugstr_w(section_name));
1000 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1001 section_name );
1002 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1003 this is not an error on application's level.*/
1005 else if (!value) /* Delete a key */
1007 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1008 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1009 section_name, key_name );
1010 return TRUE; /* same error handling as above */
1012 else /* Set the key value */
1014 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1015 key_name, TRUE, create_always );
1016 TRACE("(%s,%s,%s):\n",
1017 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1018 if (!key) return FALSE;
1020 /* strip the leading spaces. We can safely strip \n\r and
1021 * friends too, they should not happen here anyway. */
1022 while (PROFILE_isspaceW(*value)) value++;
1024 if (key->value)
1026 if (!strcmpW( key->value, value ))
1028 TRACE(" no change needed\n" );
1029 return TRUE; /* No change needed */
1031 TRACE(" replacing %s\n", debugstr_w(key->value) );
1032 HeapFree( GetProcessHeap(), 0, key->value );
1034 else TRACE(" creating key\n" );
1035 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1036 strcpyW( key->value, value );
1037 CurProfile->changed = TRUE;
1039 return TRUE;
1043 /********************* API functions **********************************/
1046 /***********************************************************************
1047 * GetProfileIntA (KERNEL32.@)
1049 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1051 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1054 /***********************************************************************
1055 * GetProfileIntW (KERNEL32.@)
1057 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1059 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1063 * if allow_section_name_copy is TRUE, allow the copying :
1064 * - of Section names if 'section' is NULL
1065 * - of Keys in a Section if 'entry' is NULL
1066 * (see MSDN doc for GetPrivateProfileString)
1068 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1069 LPCWSTR def_val, LPWSTR buffer,
1070 UINT len, LPCWSTR filename,
1071 BOOL allow_section_name_copy )
1073 int ret;
1074 LPCWSTR pDefVal = NULL;
1076 if (!filename)
1077 filename = wininiW;
1079 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1080 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1082 /* strip any trailing ' ' of def_val. */
1083 if (def_val)
1085 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1087 while (p > def_val)
1089 p--;
1090 if ((*p) != ' ')
1091 break;
1093 if (*p == ' ') /* ouch, contained trailing ' ' */
1095 int len = (int)(p - def_val);
1096 LPWSTR p;
1098 p = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1099 memcpy(p, def_val, len * sizeof(WCHAR));
1100 p[len] = '\0';
1101 pDefVal = p;
1104 if (!pDefVal)
1105 pDefVal = (LPCWSTR)def_val;
1107 RtlEnterCriticalSection( &PROFILE_CritSect );
1109 if (PROFILE_Open( filename )) {
1110 if ((allow_section_name_copy) && (section == NULL))
1111 ret = PROFILE_GetSectionNames(buffer, len);
1112 else
1113 /* PROFILE_GetString already handles the 'entry == NULL' case */
1114 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1115 } else {
1116 lstrcpynW( buffer, pDefVal, len );
1117 ret = strlenW( buffer );
1120 RtlLeaveCriticalSection( &PROFILE_CritSect );
1122 if (pDefVal != def_val) /* allocated */
1123 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1125 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1127 return ret;
1130 /***********************************************************************
1131 * GetPrivateProfileString (KERNEL.128)
1133 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1134 LPCSTR def_val, LPSTR buffer,
1135 UINT16 len, LPCSTR filename )
1137 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1138 LPWSTR bufferW;
1139 INT16 retW, ret = 0;
1141 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1142 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1143 else sectionW.Buffer = NULL;
1144 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1145 else entryW.Buffer = NULL;
1146 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1147 else def_valW.Buffer = NULL;
1148 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1149 else filenameW.Buffer = NULL;
1151 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1152 def_valW.Buffer, bufferW, len,
1153 filenameW.Buffer, FALSE );
1154 if (len)
1156 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1157 if (!ret)
1159 ret = len - 1;
1160 buffer[ret] = 0;
1162 else
1163 ret--; /* strip terminating 0 */
1166 RtlFreeUnicodeString(&sectionW);
1167 RtlFreeUnicodeString(&entryW);
1168 RtlFreeUnicodeString(&def_valW);
1169 RtlFreeUnicodeString(&filenameW);
1170 HeapFree(GetProcessHeap(), 0, bufferW);
1171 return ret;
1174 /***********************************************************************
1175 * GetPrivateProfileStringA (KERNEL32.@)
1177 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1178 LPCSTR def_val, LPSTR buffer,
1179 UINT len, LPCSTR filename )
1181 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1182 LPWSTR bufferW;
1183 INT retW, ret = 0;
1185 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1186 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1187 else sectionW.Buffer = NULL;
1188 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1189 else entryW.Buffer = NULL;
1190 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1191 else def_valW.Buffer = NULL;
1192 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1193 else filenameW.Buffer = NULL;
1195 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1196 def_valW.Buffer, bufferW, len,
1197 filenameW.Buffer);
1198 if (len)
1200 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1201 if (!ret)
1203 ret = len - 1;
1204 buffer[ret] = 0;
1206 else
1207 ret--; /* strip terminating 0 */
1210 RtlFreeUnicodeString(&sectionW);
1211 RtlFreeUnicodeString(&entryW);
1212 RtlFreeUnicodeString(&def_valW);
1213 RtlFreeUnicodeString(&filenameW);
1214 HeapFree(GetProcessHeap(), 0, bufferW);
1215 return ret;
1218 /***********************************************************************
1219 * GetPrivateProfileStringW (KERNEL32.@)
1221 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1222 LPCWSTR def_val, LPWSTR buffer,
1223 UINT len, LPCWSTR filename )
1225 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1227 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1228 buffer, len, filename, TRUE );
1231 /***********************************************************************
1232 * GetProfileStringA (KERNEL32.@)
1234 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1235 LPSTR buffer, UINT len )
1237 return GetPrivateProfileStringA( section, entry, def_val,
1238 buffer, len, "win.ini" );
1241 /***********************************************************************
1242 * GetProfileStringW (KERNEL32.@)
1244 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1245 LPCWSTR def_val, LPWSTR buffer, UINT len )
1247 return GetPrivateProfileStringW( section, entry, def_val,
1248 buffer, len, wininiW );
1251 /***********************************************************************
1252 * WriteProfileStringA (KERNEL32.@)
1254 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1255 LPCSTR string )
1257 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1260 /***********************************************************************
1261 * WriteProfileStringW (KERNEL32.@)
1263 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1264 LPCWSTR string )
1266 return WritePrivateProfileStringW( section, entry, string, wininiW );
1270 /***********************************************************************
1271 * GetPrivateProfileIntW (KERNEL32.@)
1273 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1274 INT def_val, LPCWSTR filename )
1276 WCHAR buffer[30];
1277 UNICODE_STRING bufferW;
1278 INT len;
1279 ULONG result;
1281 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1282 buffer, sizeof(buffer)/sizeof(WCHAR),
1283 filename )))
1284 return def_val;
1286 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1288 /* FIXME: if entry can be found but it's empty, then Win16 is
1289 * supposed to return 0 instead of def_val ! Difficult/problematic
1290 * to implement (every other failure also returns zero buffer),
1291 * thus wait until testing framework avail for making sure nothing
1292 * else gets broken that way. */
1293 if (!buffer[0]) return (UINT)def_val;
1295 RtlInitUnicodeString( &bufferW, buffer );
1296 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1297 return result;
1300 /***********************************************************************
1301 * GetPrivateProfileIntA (KERNEL32.@)
1303 * FIXME: rewrite using unicode
1305 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1306 INT def_val, LPCSTR filename )
1308 UNICODE_STRING entryW, filenameW, sectionW;
1309 UINT res;
1310 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1311 else entryW.Buffer = NULL;
1312 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1313 else filenameW.Buffer = NULL;
1314 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1315 else sectionW.Buffer = NULL;
1316 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1317 filenameW.Buffer);
1318 RtlFreeUnicodeString(&sectionW);
1319 RtlFreeUnicodeString(&filenameW);
1320 RtlFreeUnicodeString(&entryW);
1321 return res;
1324 /***********************************************************************
1325 * GetPrivateProfileSectionW (KERNEL32.@)
1327 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1328 DWORD len, LPCWSTR filename )
1330 int ret = 0;
1332 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1334 RtlEnterCriticalSection( &PROFILE_CritSect );
1336 if (PROFILE_Open( filename ))
1337 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1339 RtlLeaveCriticalSection( &PROFILE_CritSect );
1341 return ret;
1344 /***********************************************************************
1345 * GetPrivateProfileSectionA (KERNEL32.@)
1347 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1348 DWORD len, LPCSTR filename )
1350 UNICODE_STRING sectionW, filenameW;
1351 LPWSTR bufferW;
1352 INT retW, ret = 0;
1354 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1355 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1356 else sectionW.Buffer = NULL;
1357 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1358 else filenameW.Buffer = NULL;
1360 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1361 if (len > 2)
1363 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1364 if (ret > 2)
1365 ret -= 2;
1366 else
1368 ret = 0;
1369 buffer[len-2] = 0;
1370 buffer[len-1] = 0;
1373 else
1375 buffer[0] = 0;
1376 buffer[1] = 0;
1379 RtlFreeUnicodeString(&sectionW);
1380 RtlFreeUnicodeString(&filenameW);
1381 HeapFree(GetProcessHeap(), 0, bufferW);
1382 return ret;
1385 /***********************************************************************
1386 * GetProfileSectionA (KERNEL32.@)
1388 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1390 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1393 /***********************************************************************
1394 * GetProfileSectionW (KERNEL32.@)
1396 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1398 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1402 /***********************************************************************
1403 * WritePrivateProfileStringW (KERNEL32.@)
1405 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1406 LPCWSTR string, LPCWSTR filename )
1408 BOOL ret = FALSE;
1410 RtlEnterCriticalSection( &PROFILE_CritSect );
1412 if (!section && !entry && !string) /* documented "file flush" case */
1414 if (!filename || PROFILE_Open( filename ))
1416 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1419 else if (PROFILE_Open( filename ))
1421 if (!section) {
1422 FIXME("(NULL?,%s,%s,%s)?\n",
1423 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1424 } else {
1425 ret = PROFILE_SetString( section, entry, string, FALSE);
1426 PROFILE_FlushFile();
1430 RtlLeaveCriticalSection( &PROFILE_CritSect );
1431 return ret;
1434 /***********************************************************************
1435 * WritePrivateProfileStringA (KERNEL32.@)
1437 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1438 LPCSTR string, LPCSTR filename )
1440 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1441 BOOL ret;
1443 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1444 else sectionW.Buffer = NULL;
1445 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1446 else entryW.Buffer = NULL;
1447 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1448 else stringW.Buffer = NULL;
1449 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1450 else filenameW.Buffer = NULL;
1452 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1453 stringW.Buffer, filenameW.Buffer);
1454 RtlFreeUnicodeString(&sectionW);
1455 RtlFreeUnicodeString(&entryW);
1456 RtlFreeUnicodeString(&stringW);
1457 RtlFreeUnicodeString(&filenameW);
1458 return ret;
1461 /***********************************************************************
1462 * WritePrivateProfileSectionW (KERNEL32.@)
1464 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1465 LPCWSTR string, LPCWSTR filename )
1467 BOOL ret = FALSE;
1468 LPWSTR p;
1470 RtlEnterCriticalSection( &PROFILE_CritSect );
1472 if (!section && !string)
1474 if (!filename || PROFILE_Open( filename ))
1476 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1479 else if (PROFILE_Open( filename )) {
1480 if (!string) {/* delete the named section*/
1481 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1482 PROFILE_FlushFile();
1483 } else {
1484 PROFILE_DeleteAllKeys(section);
1485 ret = TRUE;
1486 while(*string) {
1487 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1488 strcpyW( buf, string );
1489 if((p = strchrW( buf, '='))) {
1490 *p='\0';
1491 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1493 HeapFree( GetProcessHeap(), 0, buf );
1494 string += strlenW(string)+1;
1496 PROFILE_FlushFile();
1500 RtlLeaveCriticalSection( &PROFILE_CritSect );
1501 return ret;
1504 /***********************************************************************
1505 * WritePrivateProfileSectionA (KERNEL32.@)
1507 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1508 LPCSTR string, LPCSTR filename)
1511 UNICODE_STRING sectionW, filenameW;
1512 LPWSTR stringW;
1513 BOOL ret;
1515 if (string)
1517 INT lenA, lenW;
1518 LPCSTR p = string;
1520 while(*p) p += strlen(p) + 1;
1521 lenA = p - string + 1;
1522 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1523 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1524 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1526 else stringW = NULL;
1527 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1528 else sectionW.Buffer = NULL;
1529 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1530 else filenameW.Buffer = NULL;
1532 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1534 HeapFree(GetProcessHeap(), 0, stringW);
1535 RtlFreeUnicodeString(&sectionW);
1536 RtlFreeUnicodeString(&filenameW);
1537 return ret;
1540 /***********************************************************************
1541 * WriteProfileSectionA (KERNEL32.@)
1543 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1546 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1549 /***********************************************************************
1550 * WriteProfileSectionW (KERNEL32.@)
1552 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1554 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1558 /***********************************************************************
1559 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1561 * Returns the section names contained in the specified file.
1562 * FIXME: Where do we find this file when the path is relative?
1563 * The section names are returned as a list of strings with an extra
1564 * '\0' to mark the end of the list. Except for that the behavior
1565 * depends on the Windows version.
1567 * Win95:
1568 * - if the buffer is 0 or 1 character long then it is as if it was of
1569 * infinite length.
1570 * - otherwise, if the buffer is too small only the section names that fit
1571 * are returned.
1572 * - note that this means if the buffer was too small to return even just
1573 * the first section name then a single '\0' will be returned.
1574 * - the return value is the number of characters written in the buffer,
1575 * except if the buffer was too small in which case len-2 is returned
1577 * Win2000:
1578 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1579 * '\0' and the return value is 0
1580 * - otherwise if the buffer is too small then the first section name that
1581 * does not fit is truncated so that the string list can be terminated
1582 * correctly (double '\0')
1583 * - the return value is the number of characters written in the buffer
1584 * except for the trailing '\0'. If the buffer is too small, then the
1585 * return value is len-2
1586 * - Win2000 has a bug that triggers when the section names and the
1587 * trailing '\0' fit exactly in the buffer. In that case the trailing
1588 * '\0' is missing.
1590 * Wine implements the observed Win2000 behavior (except for the bug).
1592 * Note that when the buffer is big enough then the return value may be any
1593 * value between 1 and len-1 (or len in Win95), including len-2.
1595 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1596 LPCWSTR filename)
1598 DWORD ret = 0;
1600 RtlEnterCriticalSection( &PROFILE_CritSect );
1602 if (PROFILE_Open( filename ))
1603 ret = PROFILE_GetSectionNames(buffer, size);
1605 RtlLeaveCriticalSection( &PROFILE_CritSect );
1607 return ret;
1611 /***********************************************************************
1612 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1614 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1615 LPCSTR filename)
1617 UNICODE_STRING filenameW;
1618 LPWSTR bufferW;
1619 INT retW, ret = 0;
1621 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1622 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1623 else filenameW.Buffer = NULL;
1625 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1626 if (retW && size)
1628 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1629 if (!ret)
1631 ret = size;
1632 buffer[size-1] = 0;
1636 RtlFreeUnicodeString(&filenameW);
1637 HeapFree(GetProcessHeap(), 0, bufferW);
1638 return ret;
1641 /***********************************************************************
1642 * GetPrivateProfileStructW (KERNEL32.@)
1644 * Should match Win95's behaviour pretty much
1646 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1647 LPVOID buf, UINT len, LPCWSTR filename)
1649 BOOL ret = FALSE;
1651 RtlEnterCriticalSection( &PROFILE_CritSect );
1653 if (PROFILE_Open( filename )) {
1654 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1655 if (k) {
1656 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1657 if (((strlenW(k->value) - 2) / 2) == len)
1659 LPWSTR end, p;
1660 BOOL valid = TRUE;
1661 WCHAR c;
1662 DWORD chksum = 0;
1664 end = k->value + strlenW(k->value); /* -> '\0' */
1665 /* check for invalid chars in ASCII coded hex string */
1666 for (p=k->value; p < end; p++)
1668 if (!isxdigitW(*p))
1670 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1671 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1672 valid = FALSE;
1673 break;
1676 if (valid)
1678 BOOL highnibble = TRUE;
1679 BYTE b = 0, val;
1680 LPBYTE binbuf = (LPBYTE)buf;
1682 end -= 2; /* don't include checksum in output data */
1683 /* translate ASCII hex format into binary data */
1684 for (p=k->value; p < end; p++)
1686 c = toupperW(*p);
1687 val = (c > '9') ?
1688 (c - 'A' + 10) : (c - '0');
1690 if (highnibble)
1691 b = val << 4;
1692 else
1694 b += val;
1695 *binbuf++ = b; /* feed binary data into output */
1696 chksum += b; /* calculate checksum */
1698 highnibble ^= 1; /* toggle */
1700 /* retrieve stored checksum value */
1701 c = toupperW(*p++);
1702 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1703 c = toupperW(*p);
1704 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1705 if (b == (chksum & 0xff)) /* checksums match ? */
1706 ret = TRUE;
1711 RtlLeaveCriticalSection( &PROFILE_CritSect );
1713 return ret;
1716 /***********************************************************************
1717 * GetPrivateProfileStructA (KERNEL32.@)
1719 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1720 LPVOID buffer, UINT len, LPCSTR filename)
1722 UNICODE_STRING sectionW, keyW, filenameW;
1723 INT ret;
1725 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1726 else sectionW.Buffer = NULL;
1727 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1728 else keyW.Buffer = NULL;
1729 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1730 else filenameW.Buffer = NULL;
1732 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1733 filenameW.Buffer);
1734 /* Do not translate binary data. */
1736 RtlFreeUnicodeString(&sectionW);
1737 RtlFreeUnicodeString(&keyW);
1738 RtlFreeUnicodeString(&filenameW);
1739 return ret;
1744 /***********************************************************************
1745 * WritePrivateProfileStructW (KERNEL32.@)
1747 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1748 LPVOID buf, UINT bufsize, LPCWSTR filename)
1750 BOOL ret = FALSE;
1751 LPBYTE binbuf;
1752 LPWSTR outstring, p;
1753 DWORD sum = 0;
1755 if (!section && !key && !buf) /* flush the cache */
1756 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1758 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1759 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1760 p = outstring;
1761 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1762 *p++ = hex[*binbuf >> 4];
1763 *p++ = hex[*binbuf & 0xf];
1764 sum += *binbuf;
1766 /* checksum is sum & 0xff */
1767 *p++ = hex[(sum & 0xf0) >> 4];
1768 *p++ = hex[sum & 0xf];
1769 *p++ = '\0';
1771 RtlEnterCriticalSection( &PROFILE_CritSect );
1773 if (PROFILE_Open( filename )) {
1774 ret = PROFILE_SetString( section, key, outstring, FALSE);
1775 PROFILE_FlushFile();
1778 RtlLeaveCriticalSection( &PROFILE_CritSect );
1780 HeapFree( GetProcessHeap(), 0, outstring );
1782 return ret;
1785 /***********************************************************************
1786 * WritePrivateProfileStructA (KERNEL32.@)
1788 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1789 LPVOID buf, UINT bufsize, LPCSTR filename)
1791 UNICODE_STRING sectionW, keyW, filenameW;
1792 INT ret;
1794 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1795 else sectionW.Buffer = NULL;
1796 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1797 else keyW.Buffer = NULL;
1798 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1799 else filenameW.Buffer = NULL;
1801 /* Do not translate binary data. */
1802 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1803 filenameW.Buffer);
1805 RtlFreeUnicodeString(&sectionW);
1806 RtlFreeUnicodeString(&keyW);
1807 RtlFreeUnicodeString(&filenameW);
1808 return ret;
1812 /***********************************************************************
1813 * WriteOutProfiles (KERNEL.315)
1815 void WINAPI WriteOutProfiles16(void)
1817 RtlEnterCriticalSection( &PROFILE_CritSect );
1818 PROFILE_FlushFile();
1819 RtlLeaveCriticalSection( &PROFILE_CritSect );
1822 /***********************************************************************
1823 * CloseProfileUserMapping (KERNEL32.@)
1825 BOOL WINAPI CloseProfileUserMapping(void) {
1826 FIXME("(), stub!\n");
1827 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1828 return FALSE;