The BOM doesn't need a DWORD sized variable, only a WCHAR one.
[wine.git] / dlls / kernel / profile.c
blobb704dd0b31bec6ac2f41808d776b750f99e9e528
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, { (DWORD_PTR)(__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 WCHAR 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;
377 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
378 break;
379 case ENCODING_UTF16BE:
380 TRACE("UTF16 Big Endian encoding\n");
381 szFile = (WCHAR *)pBuffer;
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, BOOL return_noequalkeys )
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_noequalkeys && !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, BOOL win32 )
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, !win32);
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 win32, copy:
1064 * - Section names if 'section' is NULL
1065 * - 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 win32 )
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 = def_val;
1107 RtlEnterCriticalSection( &PROFILE_CritSect );
1109 if (PROFILE_Open( filename )) {
1110 if (win32 && (section == NULL))
1111 ret = PROFILE_GetSectionNames(buffer, len);
1112 else
1113 /* PROFILE_GetString can handle the 'entry == NULL' case */
1114 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len, win32 );
1115 } else if (buffer && pDefVal) {
1116 lstrcpynW( buffer, pDefVal, len );
1117 ret = strlenW( buffer );
1119 else
1120 ret = 0;
1122 RtlLeaveCriticalSection( &PROFILE_CritSect );
1124 if (pDefVal != def_val) /* allocated */
1125 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1127 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1129 return ret;
1132 /***********************************************************************
1133 * GetPrivateProfileString (KERNEL.128)
1135 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1136 LPCSTR def_val, LPSTR buffer,
1137 UINT16 len, LPCSTR filename )
1139 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1140 LPWSTR bufferW;
1141 INT16 retW, ret = 0;
1143 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1144 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1145 else sectionW.Buffer = NULL;
1146 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1147 else entryW.Buffer = NULL;
1148 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1149 else def_valW.Buffer = NULL;
1150 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1151 else filenameW.Buffer = NULL;
1153 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1154 def_valW.Buffer, bufferW, len,
1155 filenameW.Buffer, FALSE );
1156 if (len)
1158 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1159 if (!ret)
1161 ret = len - 1;
1162 buffer[ret] = 0;
1164 else
1165 ret--; /* strip terminating 0 */
1168 RtlFreeUnicodeString(&sectionW);
1169 RtlFreeUnicodeString(&entryW);
1170 RtlFreeUnicodeString(&def_valW);
1171 RtlFreeUnicodeString(&filenameW);
1172 HeapFree(GetProcessHeap(), 0, bufferW);
1173 return ret;
1176 /***********************************************************************
1177 * GetPrivateProfileStringA (KERNEL32.@)
1179 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1180 LPCSTR def_val, LPSTR buffer,
1181 UINT len, LPCSTR filename )
1183 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1184 LPWSTR bufferW;
1185 INT retW, ret = 0;
1187 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1188 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1189 else sectionW.Buffer = NULL;
1190 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1191 else entryW.Buffer = NULL;
1192 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1193 else def_valW.Buffer = NULL;
1194 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1195 else filenameW.Buffer = NULL;
1197 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1198 def_valW.Buffer, bufferW, len,
1199 filenameW.Buffer);
1200 if (len)
1202 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1203 if (!ret)
1205 ret = len - 1;
1206 buffer[ret] = 0;
1208 else
1209 ret--; /* strip terminating 0 */
1212 RtlFreeUnicodeString(&sectionW);
1213 RtlFreeUnicodeString(&entryW);
1214 RtlFreeUnicodeString(&def_valW);
1215 RtlFreeUnicodeString(&filenameW);
1216 HeapFree(GetProcessHeap(), 0, bufferW);
1217 return ret;
1220 /***********************************************************************
1221 * GetPrivateProfileStringW (KERNEL32.@)
1223 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1224 LPCWSTR def_val, LPWSTR buffer,
1225 UINT len, LPCWSTR filename )
1227 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1229 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1230 buffer, len, filename, TRUE );
1233 /***********************************************************************
1234 * GetProfileStringA (KERNEL32.@)
1236 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1237 LPSTR buffer, UINT len )
1239 return GetPrivateProfileStringA( section, entry, def_val,
1240 buffer, len, "win.ini" );
1243 /***********************************************************************
1244 * GetProfileStringW (KERNEL32.@)
1246 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1247 LPCWSTR def_val, LPWSTR buffer, UINT len )
1249 return GetPrivateProfileStringW( section, entry, def_val,
1250 buffer, len, wininiW );
1253 /***********************************************************************
1254 * WriteProfileStringA (KERNEL32.@)
1256 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1257 LPCSTR string )
1259 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1262 /***********************************************************************
1263 * WriteProfileStringW (KERNEL32.@)
1265 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1266 LPCWSTR string )
1268 return WritePrivateProfileStringW( section, entry, string, wininiW );
1272 /***********************************************************************
1273 * GetPrivateProfileIntW (KERNEL32.@)
1275 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1276 INT def_val, LPCWSTR filename )
1278 WCHAR buffer[30];
1279 UNICODE_STRING bufferW;
1280 INT len;
1281 ULONG result;
1283 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1284 buffer, sizeof(buffer)/sizeof(WCHAR),
1285 filename )))
1286 return def_val;
1288 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1290 /* FIXME: if entry can be found but it's empty, then Win16 is
1291 * supposed to return 0 instead of def_val ! Difficult/problematic
1292 * to implement (every other failure also returns zero buffer),
1293 * thus wait until testing framework avail for making sure nothing
1294 * else gets broken that way. */
1295 if (!buffer[0]) return (UINT)def_val;
1297 RtlInitUnicodeString( &bufferW, buffer );
1298 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1299 return result;
1302 /***********************************************************************
1303 * GetPrivateProfileIntA (KERNEL32.@)
1305 * FIXME: rewrite using unicode
1307 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1308 INT def_val, LPCSTR filename )
1310 UNICODE_STRING entryW, filenameW, sectionW;
1311 UINT res;
1312 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1313 else entryW.Buffer = NULL;
1314 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1315 else filenameW.Buffer = NULL;
1316 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1317 else sectionW.Buffer = NULL;
1318 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1319 filenameW.Buffer);
1320 RtlFreeUnicodeString(&sectionW);
1321 RtlFreeUnicodeString(&filenameW);
1322 RtlFreeUnicodeString(&entryW);
1323 return res;
1326 /***********************************************************************
1327 * GetPrivateProfileSectionW (KERNEL32.@)
1329 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1330 DWORD len, LPCWSTR filename )
1332 int ret = 0;
1334 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1336 RtlEnterCriticalSection( &PROFILE_CritSect );
1338 if (PROFILE_Open( filename ))
1339 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE, FALSE);
1341 RtlLeaveCriticalSection( &PROFILE_CritSect );
1343 return ret;
1346 /***********************************************************************
1347 * GetPrivateProfileSectionA (KERNEL32.@)
1349 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1350 DWORD len, LPCSTR filename )
1352 UNICODE_STRING sectionW, filenameW;
1353 LPWSTR bufferW;
1354 INT retW, ret = 0;
1356 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1357 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1358 else sectionW.Buffer = NULL;
1359 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1360 else filenameW.Buffer = NULL;
1362 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1363 if (len > 2)
1365 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1366 if (ret > 2)
1367 ret -= 2;
1368 else
1370 ret = 0;
1371 buffer[len-2] = 0;
1372 buffer[len-1] = 0;
1375 else
1377 buffer[0] = 0;
1378 buffer[1] = 0;
1381 RtlFreeUnicodeString(&sectionW);
1382 RtlFreeUnicodeString(&filenameW);
1383 HeapFree(GetProcessHeap(), 0, bufferW);
1384 return ret;
1387 /***********************************************************************
1388 * GetProfileSectionA (KERNEL32.@)
1390 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1392 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1395 /***********************************************************************
1396 * GetProfileSectionW (KERNEL32.@)
1398 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1400 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1404 /***********************************************************************
1405 * WritePrivateProfileStringW (KERNEL32.@)
1407 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1408 LPCWSTR string, LPCWSTR filename )
1410 BOOL ret = FALSE;
1412 RtlEnterCriticalSection( &PROFILE_CritSect );
1414 if (!section && !entry && !string) /* documented "file flush" case */
1416 if (!filename || PROFILE_Open( filename ))
1418 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1421 else if (PROFILE_Open( filename ))
1423 if (!section) {
1424 FIXME("(NULL?,%s,%s,%s)?\n",
1425 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1426 } else {
1427 ret = PROFILE_SetString( section, entry, string, FALSE);
1428 PROFILE_FlushFile();
1432 RtlLeaveCriticalSection( &PROFILE_CritSect );
1433 return ret;
1436 /***********************************************************************
1437 * WritePrivateProfileStringA (KERNEL32.@)
1439 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1440 LPCSTR string, LPCSTR filename )
1442 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1443 BOOL ret;
1445 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1446 else sectionW.Buffer = NULL;
1447 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1448 else entryW.Buffer = NULL;
1449 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1450 else stringW.Buffer = NULL;
1451 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1452 else filenameW.Buffer = NULL;
1454 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1455 stringW.Buffer, filenameW.Buffer);
1456 RtlFreeUnicodeString(&sectionW);
1457 RtlFreeUnicodeString(&entryW);
1458 RtlFreeUnicodeString(&stringW);
1459 RtlFreeUnicodeString(&filenameW);
1460 return ret;
1463 /***********************************************************************
1464 * WritePrivateProfileSectionW (KERNEL32.@)
1466 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1467 LPCWSTR string, LPCWSTR filename )
1469 BOOL ret = FALSE;
1470 LPWSTR p;
1472 RtlEnterCriticalSection( &PROFILE_CritSect );
1474 if (!section && !string)
1476 if (!filename || PROFILE_Open( filename ))
1478 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1481 else if (PROFILE_Open( filename )) {
1482 if (!string) {/* delete the named section*/
1483 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1484 PROFILE_FlushFile();
1485 } else {
1486 PROFILE_DeleteAllKeys(section);
1487 ret = TRUE;
1488 while(*string) {
1489 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1490 strcpyW( buf, string );
1491 if((p = strchrW( buf, '='))) {
1492 *p='\0';
1493 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1495 HeapFree( GetProcessHeap(), 0, buf );
1496 string += strlenW(string)+1;
1498 PROFILE_FlushFile();
1502 RtlLeaveCriticalSection( &PROFILE_CritSect );
1503 return ret;
1506 /***********************************************************************
1507 * WritePrivateProfileSectionA (KERNEL32.@)
1509 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1510 LPCSTR string, LPCSTR filename)
1513 UNICODE_STRING sectionW, filenameW;
1514 LPWSTR stringW;
1515 BOOL ret;
1517 if (string)
1519 INT lenA, lenW;
1520 LPCSTR p = string;
1522 while(*p) p += strlen(p) + 1;
1523 lenA = p - string + 1;
1524 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1525 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1526 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1528 else stringW = NULL;
1529 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1530 else sectionW.Buffer = NULL;
1531 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1532 else filenameW.Buffer = NULL;
1534 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1536 HeapFree(GetProcessHeap(), 0, stringW);
1537 RtlFreeUnicodeString(&sectionW);
1538 RtlFreeUnicodeString(&filenameW);
1539 return ret;
1542 /***********************************************************************
1543 * WriteProfileSectionA (KERNEL32.@)
1545 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1548 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1551 /***********************************************************************
1552 * WriteProfileSectionW (KERNEL32.@)
1554 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1556 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1560 /***********************************************************************
1561 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1563 * Returns the section names contained in the specified file.
1564 * FIXME: Where do we find this file when the path is relative?
1565 * The section names are returned as a list of strings with an extra
1566 * '\0' to mark the end of the list. Except for that the behavior
1567 * depends on the Windows version.
1569 * Win95:
1570 * - if the buffer is 0 or 1 character long then it is as if it was of
1571 * infinite length.
1572 * - otherwise, if the buffer is too small only the section names that fit
1573 * are returned.
1574 * - note that this means if the buffer was too small to return even just
1575 * the first section name then a single '\0' will be returned.
1576 * - the return value is the number of characters written in the buffer,
1577 * except if the buffer was too small in which case len-2 is returned
1579 * Win2000:
1580 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1581 * '\0' and the return value is 0
1582 * - otherwise if the buffer is too small then the first section name that
1583 * does not fit is truncated so that the string list can be terminated
1584 * correctly (double '\0')
1585 * - the return value is the number of characters written in the buffer
1586 * except for the trailing '\0'. If the buffer is too small, then the
1587 * return value is len-2
1588 * - Win2000 has a bug that triggers when the section names and the
1589 * trailing '\0' fit exactly in the buffer. In that case the trailing
1590 * '\0' is missing.
1592 * Wine implements the observed Win2000 behavior (except for the bug).
1594 * Note that when the buffer is big enough then the return value may be any
1595 * value between 1 and len-1 (or len in Win95), including len-2.
1597 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1598 LPCWSTR filename)
1600 DWORD ret = 0;
1602 RtlEnterCriticalSection( &PROFILE_CritSect );
1604 if (PROFILE_Open( filename ))
1605 ret = PROFILE_GetSectionNames(buffer, size);
1607 RtlLeaveCriticalSection( &PROFILE_CritSect );
1609 return ret;
1613 /***********************************************************************
1614 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1616 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1617 LPCSTR filename)
1619 UNICODE_STRING filenameW;
1620 LPWSTR bufferW;
1621 INT retW, ret = 0;
1623 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1624 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1625 else filenameW.Buffer = NULL;
1627 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1628 if (retW && size)
1630 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1631 if (!ret)
1633 ret = size;
1634 buffer[size-1] = 0;
1638 RtlFreeUnicodeString(&filenameW);
1639 HeapFree(GetProcessHeap(), 0, bufferW);
1640 return ret;
1643 /***********************************************************************
1644 * GetPrivateProfileStructW (KERNEL32.@)
1646 * Should match Win95's behaviour pretty much
1648 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1649 LPVOID buf, UINT len, LPCWSTR filename)
1651 BOOL ret = FALSE;
1653 RtlEnterCriticalSection( &PROFILE_CritSect );
1655 if (PROFILE_Open( filename )) {
1656 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1657 if (k) {
1658 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1659 if (((strlenW(k->value) - 2) / 2) == len)
1661 LPWSTR end, p;
1662 BOOL valid = TRUE;
1663 WCHAR c;
1664 DWORD chksum = 0;
1666 end = k->value + strlenW(k->value); /* -> '\0' */
1667 /* check for invalid chars in ASCII coded hex string */
1668 for (p=k->value; p < end; p++)
1670 if (!isxdigitW(*p))
1672 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1673 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1674 valid = FALSE;
1675 break;
1678 if (valid)
1680 BOOL highnibble = TRUE;
1681 BYTE b = 0, val;
1682 LPBYTE binbuf = (LPBYTE)buf;
1684 end -= 2; /* don't include checksum in output data */
1685 /* translate ASCII hex format into binary data */
1686 for (p=k->value; p < end; p++)
1688 c = toupperW(*p);
1689 val = (c > '9') ?
1690 (c - 'A' + 10) : (c - '0');
1692 if (highnibble)
1693 b = val << 4;
1694 else
1696 b += val;
1697 *binbuf++ = b; /* feed binary data into output */
1698 chksum += b; /* calculate checksum */
1700 highnibble ^= 1; /* toggle */
1702 /* retrieve stored checksum value */
1703 c = toupperW(*p++);
1704 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1705 c = toupperW(*p);
1706 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1707 if (b == (chksum & 0xff)) /* checksums match ? */
1708 ret = TRUE;
1713 RtlLeaveCriticalSection( &PROFILE_CritSect );
1715 return ret;
1718 /***********************************************************************
1719 * GetPrivateProfileStructA (KERNEL32.@)
1721 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1722 LPVOID buffer, UINT len, LPCSTR filename)
1724 UNICODE_STRING sectionW, keyW, filenameW;
1725 INT ret;
1727 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1728 else sectionW.Buffer = NULL;
1729 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1730 else keyW.Buffer = NULL;
1731 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1732 else filenameW.Buffer = NULL;
1734 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1735 filenameW.Buffer);
1736 /* Do not translate binary data. */
1738 RtlFreeUnicodeString(&sectionW);
1739 RtlFreeUnicodeString(&keyW);
1740 RtlFreeUnicodeString(&filenameW);
1741 return ret;
1746 /***********************************************************************
1747 * WritePrivateProfileStructW (KERNEL32.@)
1749 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1750 LPVOID buf, UINT bufsize, LPCWSTR filename)
1752 BOOL ret = FALSE;
1753 LPBYTE binbuf;
1754 LPWSTR outstring, p;
1755 DWORD sum = 0;
1757 if (!section && !key && !buf) /* flush the cache */
1758 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1760 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1761 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1762 p = outstring;
1763 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1764 *p++ = hex[*binbuf >> 4];
1765 *p++ = hex[*binbuf & 0xf];
1766 sum += *binbuf;
1768 /* checksum is sum & 0xff */
1769 *p++ = hex[(sum & 0xf0) >> 4];
1770 *p++ = hex[sum & 0xf];
1771 *p++ = '\0';
1773 RtlEnterCriticalSection( &PROFILE_CritSect );
1775 if (PROFILE_Open( filename )) {
1776 ret = PROFILE_SetString( section, key, outstring, FALSE);
1777 PROFILE_FlushFile();
1780 RtlLeaveCriticalSection( &PROFILE_CritSect );
1782 HeapFree( GetProcessHeap(), 0, outstring );
1784 return ret;
1787 /***********************************************************************
1788 * WritePrivateProfileStructA (KERNEL32.@)
1790 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1791 LPVOID buf, UINT bufsize, LPCSTR filename)
1793 UNICODE_STRING sectionW, keyW, filenameW;
1794 INT ret;
1796 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1797 else sectionW.Buffer = NULL;
1798 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1799 else keyW.Buffer = NULL;
1800 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1801 else filenameW.Buffer = NULL;
1803 /* Do not translate binary data. */
1804 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1805 filenameW.Buffer);
1807 RtlFreeUnicodeString(&sectionW);
1808 RtlFreeUnicodeString(&keyW);
1809 RtlFreeUnicodeString(&filenameW);
1810 return ret;
1814 /***********************************************************************
1815 * WriteOutProfiles (KERNEL.315)
1817 void WINAPI WriteOutProfiles16(void)
1819 RtlEnterCriticalSection( &PROFILE_CritSect );
1820 PROFILE_FlushFile();
1821 RtlLeaveCriticalSection( &PROFILE_CritSect );
1824 /***********************************************************************
1825 * CloseProfileUserMapping (KERNEL32.@)
1827 BOOL WINAPI CloseProfileUserMapping(void) {
1828 FIXME("(), stub!\n");
1829 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1830 return FALSE;