Return error from low level driver when necessary.
[wine.git] / dlls / kernel / profile.c
blobf303b9d89ba412d4c59fb44fb61f51476448b3b4
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <string.h>
26 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnls.h"
31 #include "winerror.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "wine/winbase16.h"
35 #include "wine/unicode.h"
36 #include "wine/server.h"
37 #include "wine/library.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(profile);
42 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
44 typedef enum
46 ENCODING_ANSI = 1,
47 ENCODING_UTF8,
48 ENCODING_UTF16LE,
49 ENCODING_UTF16BE
50 } ENCODING;
52 typedef struct tagPROFILEKEY
54 WCHAR *value;
55 struct tagPROFILEKEY *next;
56 WCHAR name[1];
57 } PROFILEKEY;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
63 WCHAR name[1];
64 } PROFILESECTION;
67 typedef struct
69 BOOL changed;
70 PROFILESECTION *section;
71 WCHAR *filename;
72 FILETIME LastWriteTime;
73 ENCODING encoding;
74 } PROFILE;
77 #define N_CACHED_PROFILES 10
79 /* Cached profile files */
80 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
82 #define CurProfile (MRUProfile[0])
84 /* Check for comments in profile */
85 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
87 static const WCHAR emptystringW[] = {0};
88 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
90 static CRITICAL_SECTION PROFILE_CritSect;
91 static CRITICAL_SECTION_DEBUG critsect_debug =
93 0, 0, &PROFILE_CritSect,
94 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
95 0, 0, { 0, (DWORD)(__FILE__ ": PROFILE_CritSect") }
97 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
99 static const char hex[16] = "0123456789ABCDEF";
101 /***********************************************************************
102 * PROFILE_CopyEntry
104 * Copy the content of an entry into a buffer, removing quotes, and possibly
105 * translating environment variables.
107 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
108 BOOL strip_quote )
110 WCHAR quote = '\0';
112 if(!buffer) return;
114 if (strip_quote && ((*value == '\'') || (*value == '\"')))
116 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
119 lstrcpynW( buffer, value, len );
120 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
123 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
124 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
126 int i;
127 USHORT * shortbuffer = (USHORT *)buffer;
128 for (i = 0; i < len; i++)
129 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
132 /* writes any necessary encoding marker to the file */
133 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
135 DWORD dwBytesWritten;
136 DWORD bom;
137 switch (encoding)
139 case ENCODING_ANSI:
140 break;
141 case ENCODING_UTF8:
142 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
143 break;
144 case ENCODING_UTF16LE:
145 bom = 0xFEFF;
146 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
147 break;
148 case ENCODING_UTF16BE:
149 bom = 0xFFFE;
150 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
151 break;
155 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
157 char * write_buffer;
158 int write_buffer_len;
159 DWORD dwBytesWritten;
161 TRACE("writing: %s\n", debugstr_wn(szLine, len));
163 switch (encoding)
165 case ENCODING_ANSI:
166 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
167 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
168 if (!write_buffer) return;
169 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
170 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
171 HeapFree(GetProcessHeap(), 0, write_buffer);
172 break;
173 case ENCODING_UTF8:
174 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
175 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
176 if (!write_buffer) return;
177 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
178 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
179 HeapFree(GetProcessHeap(), 0, write_buffer);
180 break;
181 case ENCODING_UTF16LE:
182 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
183 break;
184 case ENCODING_UTF16BE:
185 PROFILE_ByteSwapShortBuffer(szLine, len);
186 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
187 break;
188 default:
189 FIXME("encoding type %d not implemented\n", encoding);
193 /***********************************************************************
194 * PROFILE_Save
196 * Save a profile tree to a file.
198 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
200 PROFILEKEY *key;
201 WCHAR *buffer, *p;
203 PROFILE_WriteMarker(hFile, encoding);
205 for ( ; section; section = section->next)
207 int len = 0;
209 if (section->name[0]) len += strlenW(section->name) + 6;
211 for (key = section->key; key; key = key->next)
213 len += strlenW(key->name) + 2;
214 if (key->value) len += strlenW(key->value) + 1;
217 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
218 if (!buffer) return;
220 p = buffer;
221 if (section->name[0])
223 *p++ = '\r';
224 *p++ = '\n';
225 *p++ = '[';
226 strcpyW( p, section->name );
227 p += strlenW(p);
228 *p++ = ']';
229 *p++ = '\r';
230 *p++ = '\n';
232 for (key = section->key; key; key = key->next)
234 strcpyW( p, key->name );
235 p += strlenW(p);
236 if (key->value)
238 *p++ = '=';
239 strcpyW( p, key->value );
240 p += strlenW(p);
242 *p++ = '\r';
243 *p++ = '\n';
245 PROFILE_WriteLine( hFile, buffer, len, encoding );
246 HeapFree(GetProcessHeap(), 0, buffer);
251 /***********************************************************************
252 * PROFILE_Free
254 * Free a profile tree.
256 static void PROFILE_Free( PROFILESECTION *section )
258 PROFILESECTION *next_section;
259 PROFILEKEY *key, *next_key;
261 for ( ; section; section = next_section)
263 for (key = section->key; key; key = next_key)
265 next_key = key->next;
266 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
267 HeapFree( GetProcessHeap(), 0, key );
269 next_section = section->next;
270 HeapFree( GetProcessHeap(), 0, section );
274 /* returns 1 if a character white space else 0 */
275 static inline int PROFILE_isspaceW(WCHAR c)
277 if (isspaceW(c)) return 1;
278 if (c=='\r' || c==0x1a) return 1;
279 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
280 return 0;
283 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
285 DWORD flags = IS_TEXT_UNICODE_SIGNATURE |
286 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
287 IS_TEXT_UNICODE_ODD_LENGTH;
288 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
290 *len = sizeof(bom_utf8);
291 return ENCODING_UTF8;
293 RtlIsTextUnicode((void *)buffer, *len, &flags);
294 if (flags & IS_TEXT_UNICODE_SIGNATURE)
296 *len = sizeof(WCHAR);
297 return ENCODING_UTF16LE;
299 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
301 *len = sizeof(WCHAR);
302 return ENCODING_UTF16BE;
304 *len = 0;
305 return ENCODING_ANSI;
308 static const WCHAR * PROFILE_GetLine(const WCHAR * szStart, const WCHAR * szEnd)
310 return memchrW(szStart, '\n', szEnd - szStart);
313 /***********************************************************************
314 * PROFILE_Load
316 * Load a profile tree from a file.
318 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
320 void *pBuffer;
321 WCHAR * szFile;
322 const WCHAR *szLineStart, *szLineEnd;
323 const WCHAR *szValueStart, *szNameEnd, *szEnd;
324 int line = 0, len;
325 PROFILESECTION *section, *first_section;
326 PROFILESECTION **next_section;
327 PROFILEKEY *key, *prev_key, **next_key;
328 DWORD dwFileSize;
330 TRACE("%p\n", hFile);
332 dwFileSize = GetFileSize(hFile, NULL);
333 if (dwFileSize == INVALID_FILE_SIZE)
334 return NULL;
336 pBuffer = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
337 if (!pBuffer) return NULL;
339 if (!ReadFile(hFile, pBuffer, dwFileSize, &dwFileSize, NULL))
341 HeapFree(GetProcessHeap(), 0, pBuffer);
342 WARN("Error %ld reading file\n", GetLastError());
343 return NULL;
345 len = dwFileSize;
346 *pEncoding = PROFILE_DetectTextEncoding(pBuffer, &len);
347 /* len is set to the number of bytes in the character marker.
348 * we want to skip these bytes */
349 pBuffer = (char *)pBuffer + len;
350 dwFileSize -= len;
351 switch (*pEncoding)
353 case ENCODING_ANSI:
354 TRACE("ANSI encoding\n");
356 len = MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, NULL, 0);
357 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
358 if (!szFile)
360 HeapFree(GetProcessHeap(), 0, pBuffer);
361 return NULL;
363 MultiByteToWideChar(CP_ACP, 0, (char *)pBuffer, dwFileSize, szFile, len);
364 szEnd = szFile + len;
365 break;
366 case ENCODING_UTF8:
367 TRACE("UTF8 encoding\n");
369 len = MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, NULL, 0);
370 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
371 if (!szFile)
373 HeapFree(GetProcessHeap(), 0, pBuffer);
374 return NULL;
376 MultiByteToWideChar(CP_UTF8, 0, (char *)pBuffer, dwFileSize, szFile, len);
377 szEnd = szFile + len;
378 break;
379 case ENCODING_UTF16LE:
380 TRACE("UTF16 Little Endian encoding\n");
381 szFile = (WCHAR *)pBuffer + 1;
382 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
383 break;
384 case ENCODING_UTF16BE:
385 TRACE("UTF16 Big Endian encoding\n");
386 szFile = (WCHAR *)pBuffer + 1;
387 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
388 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
389 break;
390 default:
391 FIXME("encoding type %d not implemented\n", *pEncoding);
392 HeapFree(GetProcessHeap(), 0, pBuffer);
393 return NULL;
396 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
397 if(first_section == NULL)
399 if (szFile != pBuffer)
400 HeapFree(GetProcessHeap(), 0, szFile);
401 HeapFree(GetProcessHeap(), 0, pBuffer);
402 return NULL;
404 first_section->name[0] = 0;
405 first_section->key = NULL;
406 first_section->next = NULL;
407 next_section = &first_section->next;
408 next_key = &first_section->key;
409 prev_key = NULL;
410 szLineEnd = szFile - 1; /* will be increased to correct value in loop */
412 while (TRUE)
414 szLineStart = szLineEnd + 1;
415 if (szLineStart >= szEnd)
416 break;
417 szLineEnd = PROFILE_GetLine(szLineStart, szEnd);
418 if (!szLineEnd)
419 szLineEnd = szEnd;
420 line++;
422 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
424 if (szLineStart >= szLineEnd) continue;
426 if (*szLineStart == '[') /* section start */
428 const WCHAR * szSectionEnd;
429 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
431 WARN("Invalid section header at line %d: %s\n",
432 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
434 else
436 szLineStart++;
437 len = (int)(szSectionEnd - szLineStart);
438 /* no need to allocate +1 for NULL terminating character as
439 * already included in structure */
440 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
441 break;
442 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
443 section->name[len] = '\0';
444 section->key = NULL;
445 section->next = NULL;
446 *next_section = section;
447 next_section = &section->next;
448 next_key = &section->key;
449 prev_key = NULL;
451 TRACE("New section: %s\n", debugstr_w(section->name));
453 continue;
457 /* get rid of white space at the end of the line */
458 while ((szLineEnd > szLineStart) && ((*szLineEnd == '\n') || PROFILE_isspaceW(*szLineEnd))) szLineEnd--;
460 /* line end should be pointing to character *after* the last wanted character */
461 szLineEnd++;
463 /* get rid of white space after the name and before the start
464 * of the value */
465 if ((szNameEnd = szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
467 szNameEnd = szValueStart - 1;
468 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(*szNameEnd)) szNameEnd--;
469 szValueStart++;
470 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
472 if (!szNameEnd)
473 szNameEnd = szLineEnd - 1;
474 /* name end should be pointing to character *after* the last wanted character */
475 szNameEnd++;
477 len = (int)(szNameEnd - szLineStart);
479 if (len || !prev_key || *prev_key->name)
481 /* no need to allocate +1 for NULL terminating character as
482 * already included in structure */
483 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
484 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
485 key->name[len] = '\0';
486 if (szValueStart)
488 len = (int)(szLineEnd - szValueStart);
489 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
490 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
491 key->value[len] = '\0';
493 else key->value = NULL;
495 key->next = NULL;
496 *next_key = key;
497 next_key = &key->next;
498 prev_key = key;
500 TRACE("New key: name=%s, value=%s\n",
501 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
504 if (szFile != pBuffer)
505 HeapFree(GetProcessHeap(), 0, szFile);
506 HeapFree(GetProcessHeap(), 0, pBuffer);
507 return first_section;
511 /***********************************************************************
512 * PROFILE_DeleteSection
514 * Delete a section from a profile tree.
516 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
518 while (*section)
520 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
522 PROFILESECTION *to_del = *section;
523 *section = to_del->next;
524 to_del->next = NULL;
525 PROFILE_Free( to_del );
526 return TRUE;
528 section = &(*section)->next;
530 return FALSE;
534 /***********************************************************************
535 * PROFILE_DeleteKey
537 * Delete a key from a profile tree.
539 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
540 LPCWSTR section_name, LPCWSTR key_name )
542 while (*section)
544 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
546 PROFILEKEY **key = &(*section)->key;
547 while (*key)
549 if (!strcmpiW( (*key)->name, key_name ))
551 PROFILEKEY *to_del = *key;
552 *key = to_del->next;
553 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
554 HeapFree( GetProcessHeap(), 0, to_del );
555 return TRUE;
557 key = &(*key)->next;
560 section = &(*section)->next;
562 return FALSE;
566 /***********************************************************************
567 * PROFILE_DeleteAllKeys
569 * Delete all keys from a profile tree.
571 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
573 PROFILESECTION **section= &CurProfile->section;
574 while (*section)
576 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
578 PROFILEKEY **key = &(*section)->key;
579 while (*key)
581 PROFILEKEY *to_del = *key;
582 *key = to_del->next;
583 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
584 HeapFree( GetProcessHeap(), 0, to_del );
585 CurProfile->changed =TRUE;
588 section = &(*section)->next;
593 /***********************************************************************
594 * PROFILE_Find
596 * Find a key in a profile tree, optionally creating it.
598 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
599 LPCWSTR key_name, BOOL create, BOOL create_always )
601 LPCWSTR p;
602 int seclen, keylen;
604 while (PROFILE_isspaceW(*section_name)) section_name++;
605 p = section_name + strlenW(section_name) - 1;
606 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
607 seclen = p - section_name + 1;
609 while (PROFILE_isspaceW(*key_name)) key_name++;
610 p = key_name + strlenW(key_name) - 1;
611 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
612 keylen = p - key_name + 1;
614 while (*section)
616 if ( ((*section)->name[0])
617 && (!(strncmpiW( (*section)->name, section_name, seclen )))
618 && (((*section)->name)[seclen] == '\0') )
620 PROFILEKEY **key = &(*section)->key;
622 while (*key)
624 /* If create_always is FALSE then we check if the keyname
625 * already exists. Otherwise we add it regardless of its
626 * existence, to allow keys to be added more than once in
627 * some cases.
629 if(!create_always)
631 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
632 && (((*key)->name)[keylen] == '\0') )
633 return *key;
635 key = &(*key)->next;
637 if (!create) return NULL;
638 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
639 return NULL;
640 strcpyW( (*key)->name, key_name );
641 (*key)->value = NULL;
642 (*key)->next = NULL;
643 return *key;
645 section = &(*section)->next;
647 if (!create) return NULL;
648 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
649 if(*section == NULL) return NULL;
650 strcpyW( (*section)->name, section_name );
651 (*section)->next = NULL;
652 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
653 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
655 HeapFree(GetProcessHeap(), 0, *section);
656 return NULL;
658 strcpyW( (*section)->key->name, key_name );
659 (*section)->key->value = NULL;
660 (*section)->key->next = NULL;
661 return (*section)->key;
665 /***********************************************************************
666 * PROFILE_FlushFile
668 * Flush the current profile to disk if changed.
670 static BOOL PROFILE_FlushFile(void)
672 HANDLE hFile = NULL;
673 FILETIME LastWriteTime;
675 if(!CurProfile)
677 WARN("No current profile!\n");
678 return FALSE;
681 if (!CurProfile->changed) return TRUE;
683 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
685 if (hFile == INVALID_HANDLE_VALUE)
687 WARN("could not save profile file %s (error was %ld)\n", debugstr_w(CurProfile->filename), GetLastError());
688 return FALSE;
691 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
692 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
693 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
694 CurProfile->LastWriteTime=LastWriteTime;
695 CloseHandle( hFile );
696 CurProfile->changed = FALSE;
697 return TRUE;
701 /***********************************************************************
702 * PROFILE_ReleaseFile
704 * Flush the current profile to disk and remove it from the cache.
706 static void PROFILE_ReleaseFile(void)
708 PROFILE_FlushFile();
709 PROFILE_Free( CurProfile->section );
710 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
711 CurProfile->changed = FALSE;
712 CurProfile->section = NULL;
713 CurProfile->filename = NULL;
714 CurProfile->encoding = ENCODING_ANSI;
715 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
719 /***********************************************************************
720 * PROFILE_Open
722 * Open a profile file, checking the cached file first.
724 static BOOL PROFILE_Open( LPCWSTR filename )
726 WCHAR windirW[MAX_PATH];
727 WCHAR buffer[MAX_PATH];
728 HANDLE hFile = INVALID_HANDLE_VALUE;
729 FILETIME LastWriteTime;
730 int i,j;
731 PROFILE *tempProfile;
733 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
735 /* First time around */
737 if(!CurProfile)
738 for(i=0;i<N_CACHED_PROFILES;i++)
740 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
741 if(MRUProfile[i] == NULL) break;
742 MRUProfile[i]->changed=FALSE;
743 MRUProfile[i]->section=NULL;
744 MRUProfile[i]->filename=NULL;
745 MRUProfile[i]->encoding=ENCODING_ANSI;
746 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
749 GetWindowsDirectoryW( windirW, MAX_PATH );
751 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
752 !strchrW(filename, '\\') && !strchrW(filename, '/'))
754 static const WCHAR wszSeparator[] = {'\\', 0};
755 strcpyW(buffer, windirW);
756 strcatW(buffer, wszSeparator);
757 strcatW(buffer, filename);
759 else
761 LPWSTR dummy;
762 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
765 TRACE("path: %s\n", debugstr_w(buffer));
767 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
769 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
771 WARN("Error %ld opening file %s\n", GetLastError(), debugstr_w(buffer));
772 return FALSE;
775 for(i=0;i<N_CACHED_PROFILES;i++)
777 if ((MRUProfile[i]->filename && !strcmpW( buffer, MRUProfile[i]->filename )))
779 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
780 if(i)
782 PROFILE_FlushFile();
783 tempProfile=MRUProfile[i];
784 for(j=i;j>0;j--)
785 MRUProfile[j]=MRUProfile[j-1];
786 CurProfile=tempProfile;
788 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
789 if(memcmp(&CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME)))
790 TRACE("(%s): already opened (mru=%d)\n",
791 debugstr_w(buffer), i );
792 else
793 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
794 debugstr_w(buffer), i );
795 CloseHandle(hFile);
796 return TRUE;
800 /* Flush the old current profile */
801 PROFILE_FlushFile();
803 /* Make the oldest profile the current one only in order to get rid of it */
804 if(i==N_CACHED_PROFILES)
806 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
807 for(i=N_CACHED_PROFILES-1;i>0;i--)
808 MRUProfile[i]=MRUProfile[i-1];
809 CurProfile=tempProfile;
811 if(CurProfile->filename) PROFILE_ReleaseFile();
813 /* OK, now that CurProfile is definitely free we assign it our new file */
814 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
815 strcpyW( CurProfile->filename, buffer );
817 if (hFile != INVALID_HANDLE_VALUE)
819 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
820 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
821 CloseHandle(hFile);
823 else
825 /* Does not exist yet, we will create it in PROFILE_FlushFile */
826 WARN("profile file %s not found\n", debugstr_w(buffer) );
828 return TRUE;
832 /***********************************************************************
833 * PROFILE_GetSection
835 * Returns all keys of a section.
836 * If return_values is TRUE, also include the corresponding values.
838 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
839 LPWSTR buffer, UINT len, BOOL return_values )
841 PROFILEKEY *key;
843 if(!buffer) return 0;
845 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
847 while (section)
849 if (section->name[0] && !strcmpiW( section->name, section_name ))
851 UINT oldlen = len;
852 for (key = section->key; key; key = key->next)
854 if (len <= 2) break;
855 if (!*key->name) continue; /* Skip empty lines */
856 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
857 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
858 len -= strlenW(buffer) + 1;
859 buffer += strlenW(buffer) + 1;
860 if (len < 2)
861 break;
862 if (return_values && key->value) {
863 buffer[-1] = '=';
864 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
865 len -= strlenW(buffer) + 1;
866 buffer += strlenW(buffer) + 1;
869 *buffer = '\0';
870 if (len <= 1)
871 /*If either lpszSection or lpszKey is NULL and the supplied
872 destination buffer is too small to hold all the strings,
873 the last string is truncated and followed by two null characters.
874 In this case, the return value is equal to cchReturnBuffer
875 minus two. */
877 buffer[-1] = '\0';
878 return oldlen - 2;
880 return oldlen - len;
882 section = section->next;
884 buffer[0] = buffer[1] = '\0';
885 return 0;
888 /* See GetPrivateProfileSectionNamesA for documentation */
889 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
891 LPWSTR buf;
892 UINT f,l;
893 PROFILESECTION *section;
895 TRACE("(%p, %d)\n", buffer, len);
897 if (!buffer || !len)
898 return 0;
899 if (len==1) {
900 *buffer='\0';
901 return 0;
904 f=len-1;
905 buf=buffer;
906 section = CurProfile->section;
907 while ((section!=NULL)) {
908 if (section->name[0]) {
909 l = strlenW(section->name)+1;
910 if (l > f) {
911 if (f>0) {
912 strncpyW(buf, section->name, f-1);
913 buf += f-1;
914 *buf++='\0';
916 *buf='\0';
917 return len-2;
919 strcpyW(buf, section->name);
920 buf += l;
921 f -= l;
923 section = section->next;
925 *buf='\0';
926 return buf-buffer;
930 /***********************************************************************
931 * PROFILE_GetString
933 * Get a profile string.
935 * Tests with GetPrivateProfileString16, W95a,
936 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
937 * section key_name def_val res buffer
938 * "set1" "1" "x" 43 [data]
939 * "set1" "1 " "x" 43 [data] (!)
940 * "set1" " 1 "' "x" 43 [data] (!)
941 * "set1" "" "x" 1 "x"
942 * "set1" "" "x " 1 "x" (!)
943 * "set1" "" " x " 3 " x" (!)
944 * "set1" NULL "x" 6 "1\02\03\0\0"
945 * "set1" "" "x" 1 "x"
946 * NULL "1" "x" 0 "" (!)
947 * "" "1" "x" 1 "x"
948 * NULL NULL "" 0 ""
952 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
953 LPCWSTR def_val, LPWSTR buffer, UINT len )
955 PROFILEKEY *key = NULL;
956 static const WCHAR empty_strW[] = { 0 };
958 if(!buffer) return 0;
960 if (!def_val) def_val = empty_strW;
961 if (key_name)
963 if (!key_name[0])
965 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
966 return 0;
968 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
969 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
970 len, TRUE );
971 TRACE("(%s,%s,%s): returning %s\n",
972 debugstr_w(section), debugstr_w(key_name),
973 debugstr_w(def_val), debugstr_w(buffer) );
974 return strlenW( buffer );
976 /* no "else" here ! */
977 if (section && section[0])
979 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE);
980 if (!buffer[0]) /* no luck -> def_val */
982 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
983 ret = strlenW(buffer);
985 return ret;
987 buffer[0] = '\0';
988 return 0;
992 /***********************************************************************
993 * PROFILE_SetString
995 * Set a profile string.
997 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
998 LPCWSTR value, BOOL create_always )
1000 if (!key_name) /* Delete a whole section */
1002 TRACE("(%s)\n", debugstr_w(section_name));
1003 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1004 section_name );
1005 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1006 this is not an error on application's level.*/
1008 else if (!value) /* Delete a key */
1010 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1011 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1012 section_name, key_name );
1013 return TRUE; /* same error handling as above */
1015 else /* Set the key value */
1017 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1018 key_name, TRUE, create_always );
1019 TRACE("(%s,%s,%s):\n",
1020 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1021 if (!key) return FALSE;
1023 /* strip the leading spaces. We can safely strip \n\r and
1024 * friends too, they should not happen here anyway. */
1025 while (PROFILE_isspaceW(*value)) value++;
1027 if (key->value)
1029 if (!strcmpW( key->value, value ))
1031 TRACE(" no change needed\n" );
1032 return TRUE; /* No change needed */
1034 TRACE(" replacing %s\n", debugstr_w(key->value) );
1035 HeapFree( GetProcessHeap(), 0, key->value );
1037 else TRACE(" creating key\n" );
1038 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1039 strcpyW( key->value, value );
1040 CurProfile->changed = TRUE;
1042 return TRUE;
1046 /********************* API functions **********************************/
1049 /***********************************************************************
1050 * GetProfileIntA (KERNEL32.@)
1052 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1054 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1057 /***********************************************************************
1058 * GetProfileIntW (KERNEL32.@)
1060 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1062 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1066 * if allow_section_name_copy is TRUE, allow the copying :
1067 * - of Section names if 'section' is NULL
1068 * - of Keys in a Section if 'entry' is NULL
1069 * (see MSDN doc for GetPrivateProfileString)
1071 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1072 LPCWSTR def_val, LPWSTR buffer,
1073 UINT len, LPCWSTR filename,
1074 BOOL allow_section_name_copy )
1076 int ret;
1077 LPCWSTR pDefVal = NULL;
1079 if (!filename)
1080 filename = wininiW;
1082 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1083 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1085 /* strip any trailing ' ' of def_val. */
1086 if (def_val)
1088 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1090 while (p > def_val)
1092 p--;
1093 if ((*p) != ' ')
1094 break;
1096 if (*p == ' ') /* ouch, contained trailing ' ' */
1098 int len = (int)(p - def_val);
1099 LPWSTR p;
1101 p = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1102 strncpyW(p, def_val, len);
1103 p[len] = '\0';
1104 pDefVal = p;
1107 if (!pDefVal)
1108 pDefVal = (LPCWSTR)def_val;
1110 RtlEnterCriticalSection( &PROFILE_CritSect );
1112 if (PROFILE_Open( filename )) {
1113 if ((allow_section_name_copy) && (section == NULL))
1114 ret = PROFILE_GetSectionNames(buffer, len);
1115 else
1116 /* PROFILE_GetString already handles the 'entry == NULL' case */
1117 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1118 } else {
1119 lstrcpynW( buffer, pDefVal, len );
1120 ret = strlenW( buffer );
1123 RtlLeaveCriticalSection( &PROFILE_CritSect );
1125 if (pDefVal != def_val) /* allocated */
1126 HeapFree(GetProcessHeap(), 0, (void*)pDefVal);
1128 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1130 return ret;
1133 /***********************************************************************
1134 * GetPrivateProfileString (KERNEL.128)
1136 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1137 LPCSTR def_val, LPSTR buffer,
1138 UINT16 len, LPCSTR filename )
1140 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1141 LPWSTR bufferW;
1142 INT16 retW, ret = 0;
1144 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1145 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1146 else sectionW.Buffer = NULL;
1147 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1148 else entryW.Buffer = NULL;
1149 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1150 else def_valW.Buffer = NULL;
1151 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1152 else filenameW.Buffer = NULL;
1154 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1155 def_valW.Buffer, bufferW, len,
1156 filenameW.Buffer, FALSE );
1157 if (len)
1159 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1160 if (!ret)
1162 ret = len - 1;
1163 buffer[ret] = 0;
1165 else
1166 ret--; /* strip terminating 0 */
1169 RtlFreeUnicodeString(&sectionW);
1170 RtlFreeUnicodeString(&entryW);
1171 RtlFreeUnicodeString(&def_valW);
1172 RtlFreeUnicodeString(&filenameW);
1173 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1174 return ret;
1177 /***********************************************************************
1178 * GetPrivateProfileStringA (KERNEL32.@)
1180 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1181 LPCSTR def_val, LPSTR buffer,
1182 UINT len, LPCSTR filename )
1184 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1185 LPWSTR bufferW;
1186 INT retW, ret = 0;
1188 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1189 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1190 else sectionW.Buffer = NULL;
1191 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1192 else entryW.Buffer = NULL;
1193 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1194 else def_valW.Buffer = NULL;
1195 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1196 else filenameW.Buffer = NULL;
1198 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1199 def_valW.Buffer, bufferW, len,
1200 filenameW.Buffer);
1201 if (len)
1203 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1204 if (!ret)
1206 ret = len - 1;
1207 buffer[ret] = 0;
1209 else
1210 ret--; /* strip terminating 0 */
1213 RtlFreeUnicodeString(&sectionW);
1214 RtlFreeUnicodeString(&entryW);
1215 RtlFreeUnicodeString(&def_valW);
1216 RtlFreeUnicodeString(&filenameW);
1217 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1218 return ret;
1221 /***********************************************************************
1222 * GetPrivateProfileStringW (KERNEL32.@)
1224 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1225 LPCWSTR def_val, LPWSTR buffer,
1226 UINT len, LPCWSTR filename )
1228 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1230 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1231 buffer, len, filename, TRUE );
1234 /***********************************************************************
1235 * GetProfileStringA (KERNEL32.@)
1237 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1238 LPSTR buffer, UINT len )
1240 return GetPrivateProfileStringA( section, entry, def_val,
1241 buffer, len, "win.ini" );
1244 /***********************************************************************
1245 * GetProfileStringW (KERNEL32.@)
1247 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1248 LPCWSTR def_val, LPWSTR buffer, UINT len )
1250 return GetPrivateProfileStringW( section, entry, def_val,
1251 buffer, len, wininiW );
1254 /***********************************************************************
1255 * WriteProfileStringA (KERNEL32.@)
1257 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1258 LPCSTR string )
1260 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1263 /***********************************************************************
1264 * WriteProfileStringW (KERNEL32.@)
1266 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1267 LPCWSTR string )
1269 return WritePrivateProfileStringW( section, entry, string, wininiW );
1273 /***********************************************************************
1274 * GetPrivateProfileIntW (KERNEL32.@)
1276 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1277 INT def_val, LPCWSTR filename )
1279 WCHAR buffer[30];
1280 UNICODE_STRING bufferW;
1281 INT len;
1282 ULONG result;
1284 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1285 buffer, sizeof(buffer)/sizeof(WCHAR),
1286 filename )))
1287 return def_val;
1289 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!\n");
1291 /* FIXME: if entry can be found but it's empty, then Win16 is
1292 * supposed to return 0 instead of def_val ! Difficult/problematic
1293 * to implement (every other failure also returns zero buffer),
1294 * thus wait until testing framework avail for making sure nothing
1295 * else gets broken that way. */
1296 if (!buffer[0]) return (UINT)def_val;
1298 RtlInitUnicodeString( &bufferW, buffer );
1299 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1300 return result;
1303 /***********************************************************************
1304 * GetPrivateProfileIntA (KERNEL32.@)
1306 * FIXME: rewrite using unicode
1308 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1309 INT def_val, LPCSTR filename )
1311 UNICODE_STRING entryW, filenameW, sectionW;
1312 UINT res;
1313 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1314 else entryW.Buffer = NULL;
1315 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1316 else filenameW.Buffer = NULL;
1317 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1318 else sectionW.Buffer = NULL;
1319 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1320 filenameW.Buffer);
1321 RtlFreeUnicodeString(&sectionW);
1322 RtlFreeUnicodeString(&filenameW);
1323 RtlFreeUnicodeString(&entryW);
1324 return res;
1327 /***********************************************************************
1328 * GetPrivateProfileSectionW (KERNEL32.@)
1330 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1331 DWORD len, LPCWSTR filename )
1333 int ret = 0;
1335 TRACE("(%s, %p, %ld, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1337 RtlEnterCriticalSection( &PROFILE_CritSect );
1339 if (PROFILE_Open( filename ))
1340 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE);
1342 RtlLeaveCriticalSection( &PROFILE_CritSect );
1344 return ret;
1347 /***********************************************************************
1348 * GetPrivateProfileSectionA (KERNEL32.@)
1350 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1351 DWORD len, LPCSTR filename )
1353 UNICODE_STRING sectionW, filenameW;
1354 LPWSTR bufferW;
1355 INT retW, ret = 0;
1357 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1358 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1359 else sectionW.Buffer = NULL;
1360 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1361 else filenameW.Buffer = NULL;
1363 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1364 if (len > 2)
1366 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1367 if (ret > 2)
1368 ret -= 2;
1369 else
1371 ret = 0;
1372 buffer[len-2] = 0;
1373 buffer[len-1] = 0;
1376 else
1378 buffer[0] = 0;
1379 buffer[1] = 0;
1382 RtlFreeUnicodeString(&sectionW);
1383 RtlFreeUnicodeString(&filenameW);
1384 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1385 return ret;
1388 /***********************************************************************
1389 * GetProfileSectionA (KERNEL32.@)
1391 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1393 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1396 /***********************************************************************
1397 * GetProfileSectionW (KERNEL32.@)
1399 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1401 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1405 /***********************************************************************
1406 * WritePrivateProfileStringW (KERNEL32.@)
1408 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1409 LPCWSTR string, LPCWSTR filename )
1411 BOOL ret = FALSE;
1413 RtlEnterCriticalSection( &PROFILE_CritSect );
1415 if (PROFILE_Open( filename ))
1417 if (!section && !entry && !string) /* documented "file flush" case */
1419 PROFILE_FlushFile();
1420 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1422 else {
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();
1433 RtlLeaveCriticalSection( &PROFILE_CritSect );
1434 return ret;
1437 /***********************************************************************
1438 * WritePrivateProfileStringA (KERNEL32.@)
1440 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1441 LPCSTR string, LPCSTR filename )
1443 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1444 BOOL ret;
1446 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1447 else sectionW.Buffer = NULL;
1448 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1449 else entryW.Buffer = NULL;
1450 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1451 else stringW.Buffer = NULL;
1452 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1453 else filenameW.Buffer = NULL;
1455 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1456 stringW.Buffer, filenameW.Buffer);
1457 RtlFreeUnicodeString(&sectionW);
1458 RtlFreeUnicodeString(&entryW);
1459 RtlFreeUnicodeString(&stringW);
1460 RtlFreeUnicodeString(&filenameW);
1461 return ret;
1464 /***********************************************************************
1465 * WritePrivateProfileSectionW (KERNEL32.@)
1467 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1468 LPCWSTR string, LPCWSTR filename )
1470 BOOL ret = FALSE;
1471 LPWSTR p;
1473 RtlEnterCriticalSection( &PROFILE_CritSect );
1475 if (PROFILE_Open( filename )) {
1476 if (!section && !string)
1477 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1478 else if (!string) {/* delete the named section*/
1479 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1480 PROFILE_FlushFile();
1481 } else {
1482 PROFILE_DeleteAllKeys(section);
1483 ret = TRUE;
1484 while(*string) {
1485 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1486 strcpyW( buf, string );
1487 if((p = strchrW( buf, '='))) {
1488 *p='\0';
1489 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1491 HeapFree( GetProcessHeap(), 0, buf );
1492 string += strlenW(string)+1;
1494 PROFILE_FlushFile();
1498 RtlLeaveCriticalSection( &PROFILE_CritSect );
1499 return ret;
1502 /***********************************************************************
1503 * WritePrivateProfileSectionA (KERNEL32.@)
1505 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1506 LPCSTR string, LPCSTR filename)
1509 UNICODE_STRING sectionW, filenameW;
1510 LPWSTR stringW;
1511 BOOL ret;
1513 if (string)
1515 INT lenA, lenW;
1516 LPCSTR p = string;
1518 while(*p) p += strlen(p) + 1;
1519 lenA = p - string + 1;
1520 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1521 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1522 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1524 else stringW = NULL;
1525 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1526 else sectionW.Buffer = NULL;
1527 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1528 else filenameW.Buffer = NULL;
1530 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1532 HeapFree(GetProcessHeap(), 0, stringW);
1533 RtlFreeUnicodeString(&sectionW);
1534 RtlFreeUnicodeString(&filenameW);
1535 return ret;
1538 /***********************************************************************
1539 * WriteProfileSectionA (KERNEL32.@)
1541 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1544 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1547 /***********************************************************************
1548 * WriteProfileSectionW (KERNEL32.@)
1550 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1552 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1556 /***********************************************************************
1557 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1559 * Returns the section names contained in the specified file.
1560 * FIXME: Where do we find this file when the path is relative?
1561 * The section names are returned as a list of strings with an extra
1562 * '\0' to mark the end of the list. Except for that the behavior
1563 * depends on the Windows version.
1565 * Win95:
1566 * - if the buffer is 0 or 1 character long then it is as if it was of
1567 * infinite length.
1568 * - otherwise, if the buffer is to small only the section names that fit
1569 * are returned.
1570 * - note that this means if the buffer was to small to return even just
1571 * the first section name then a single '\0' will be returned.
1572 * - the return value is the number of characters written in the buffer,
1573 * except if the buffer was too smal in which case len-2 is returned
1575 * Win2000:
1576 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1577 * '\0' and the return value is 0
1578 * - otherwise if the buffer is too small then the first section name that
1579 * does not fit is truncated so that the string list can be terminated
1580 * correctly (double '\0')
1581 * - the return value is the number of characters written in the buffer
1582 * except for the trailing '\0'. If the buffer is too small, then the
1583 * return value is len-2
1584 * - Win2000 has a bug that triggers when the section names and the
1585 * trailing '\0' fit exactly in the buffer. In that case the trailing
1586 * '\0' is missing.
1588 * Wine implements the observed Win2000 behavior (except for the bug).
1590 * Note that when the buffer is big enough then the return value may be any
1591 * value between 1 and len-1 (or len in Win95), including len-2.
1593 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1594 LPCWSTR filename)
1596 DWORD ret = 0;
1598 RtlEnterCriticalSection( &PROFILE_CritSect );
1600 if (PROFILE_Open( filename ))
1601 ret = PROFILE_GetSectionNames(buffer, size);
1603 RtlLeaveCriticalSection( &PROFILE_CritSect );
1605 return ret;
1609 /***********************************************************************
1610 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1612 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1613 LPCSTR filename)
1615 UNICODE_STRING filenameW;
1616 LPWSTR bufferW;
1617 INT retW, ret = 0;
1619 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1620 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1621 else filenameW.Buffer = NULL;
1623 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1624 if (retW && size)
1626 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1627 if (!ret)
1629 ret = size;
1630 buffer[size-1] = 0;
1634 RtlFreeUnicodeString(&filenameW);
1635 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1636 return ret;
1639 /***********************************************************************
1640 * GetPrivateProfileStructW (KERNEL32.@)
1642 * Should match Win95's behaviour pretty much
1644 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1645 LPVOID buf, UINT len, LPCWSTR filename)
1647 BOOL ret = FALSE;
1649 RtlEnterCriticalSection( &PROFILE_CritSect );
1651 if (PROFILE_Open( filename )) {
1652 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1653 if (k) {
1654 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1655 if (((strlenW(k->value) - 2) / 2) == len)
1657 LPWSTR end, p;
1658 BOOL valid = TRUE;
1659 WCHAR c;
1660 DWORD chksum = 0;
1662 end = k->value + strlenW(k->value); /* -> '\0' */
1663 /* check for invalid chars in ASCII coded hex string */
1664 for (p=k->value; p < end; p++)
1666 if (!isxdigitW(*p))
1668 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1669 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1670 valid = FALSE;
1671 break;
1674 if (valid)
1676 BOOL highnibble = TRUE;
1677 BYTE b = 0, val;
1678 LPBYTE binbuf = (LPBYTE)buf;
1680 end -= 2; /* don't include checksum in output data */
1681 /* translate ASCII hex format into binary data */
1682 for (p=k->value; p < end; p++)
1684 c = toupperW(*p);
1685 val = (c > '9') ?
1686 (c - 'A' + 10) : (c - '0');
1688 if (highnibble)
1689 b = val << 4;
1690 else
1692 b += val;
1693 *binbuf++ = b; /* feed binary data into output */
1694 chksum += b; /* calculate checksum */
1696 highnibble ^= 1; /* toggle */
1698 /* retrieve stored checksum value */
1699 c = toupperW(*p++);
1700 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1701 c = toupperW(*p);
1702 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1703 if (b == (chksum & 0xff)) /* checksums match ? */
1704 ret = TRUE;
1709 RtlLeaveCriticalSection( &PROFILE_CritSect );
1711 return ret;
1714 /***********************************************************************
1715 * GetPrivateProfileStructA (KERNEL32.@)
1717 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1718 LPVOID buffer, UINT len, LPCSTR filename)
1720 UNICODE_STRING sectionW, keyW, filenameW;
1721 INT ret;
1723 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1724 else sectionW.Buffer = NULL;
1725 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1726 else keyW.Buffer = NULL;
1727 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1728 else filenameW.Buffer = NULL;
1730 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1731 filenameW.Buffer);
1732 /* Do not translate binary data. */
1734 RtlFreeUnicodeString(&sectionW);
1735 RtlFreeUnicodeString(&keyW);
1736 RtlFreeUnicodeString(&filenameW);
1737 return ret;
1742 /***********************************************************************
1743 * WritePrivateProfileStructW (KERNEL32.@)
1745 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1746 LPVOID buf, UINT bufsize, LPCWSTR filename)
1748 BOOL ret = FALSE;
1749 LPBYTE binbuf;
1750 LPWSTR outstring, p;
1751 DWORD sum = 0;
1753 if (!section && !key && !buf) /* flush the cache */
1754 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1756 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1757 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1758 p = outstring;
1759 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1760 *p++ = hex[*binbuf >> 4];
1761 *p++ = hex[*binbuf & 0xf];
1762 sum += *binbuf;
1764 /* checksum is sum & 0xff */
1765 *p++ = hex[(sum & 0xf0) >> 4];
1766 *p++ = hex[sum & 0xf];
1767 *p++ = '\0';
1769 RtlEnterCriticalSection( &PROFILE_CritSect );
1771 if (PROFILE_Open( filename )) {
1772 ret = PROFILE_SetString( section, key, outstring, FALSE);
1773 PROFILE_FlushFile();
1776 RtlLeaveCriticalSection( &PROFILE_CritSect );
1778 HeapFree( GetProcessHeap(), 0, outstring );
1780 return ret;
1783 /***********************************************************************
1784 * WritePrivateProfileStructA (KERNEL32.@)
1786 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1787 LPVOID buf, UINT bufsize, LPCSTR filename)
1789 UNICODE_STRING sectionW, keyW, filenameW;
1790 INT ret;
1792 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1793 else sectionW.Buffer = NULL;
1794 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1795 else keyW.Buffer = NULL;
1796 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1797 else filenameW.Buffer = NULL;
1799 /* Do not translate binary data. */
1800 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1801 filenameW.Buffer);
1803 RtlFreeUnicodeString(&sectionW);
1804 RtlFreeUnicodeString(&keyW);
1805 RtlFreeUnicodeString(&filenameW);
1806 return ret;
1810 /***********************************************************************
1811 * WriteOutProfiles (KERNEL.315)
1813 void WINAPI WriteOutProfiles16(void)
1815 RtlEnterCriticalSection( &PROFILE_CritSect );
1816 PROFILE_FlushFile();
1817 RtlLeaveCriticalSection( &PROFILE_CritSect );
1820 /***********************************************************************
1821 * CloseProfileUserMapping (KERNEL32.@)
1823 BOOL WINAPI CloseProfileUserMapping(void) {
1824 FIXME("(), stub!\n");
1825 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1826 return FALSE;