push 556f62bab943c37bb6cbad95a6ecb3d73e04a93f
[wine/hacks.git] / dlls / kernel32 / profile.c
blob8a787da2c7fe52669d984d5cf3c3e2137d639073
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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/library.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(profile);
40 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
42 typedef enum
44 ENCODING_ANSI = 1,
45 ENCODING_UTF8,
46 ENCODING_UTF16LE,
47 ENCODING_UTF16BE
48 } ENCODING;
50 typedef struct tagPROFILEKEY
52 WCHAR *value;
53 struct tagPROFILEKEY *next;
54 WCHAR name[1];
55 } PROFILEKEY;
57 typedef struct tagPROFILESECTION
59 struct tagPROFILEKEY *key;
60 struct tagPROFILESECTION *next;
61 WCHAR name[1];
62 } PROFILESECTION;
65 typedef struct
67 BOOL changed;
68 PROFILESECTION *section;
69 WCHAR *filename;
70 FILETIME LastWriteTime;
71 ENCODING encoding;
72 } PROFILE;
75 #define N_CACHED_PROFILES 10
77 /* Cached profile files */
78 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
80 #define CurProfile (MRUProfile[0])
82 /* Check for comments in profile */
83 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
85 static const WCHAR emptystringW[] = {0};
86 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
88 static CRITICAL_SECTION PROFILE_CritSect;
89 static CRITICAL_SECTION_DEBUG critsect_debug =
91 0, 0, &PROFILE_CritSect,
92 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
93 0, 0, { (DWORD_PTR)(__FILE__ ": PROFILE_CritSect") }
95 static CRITICAL_SECTION PROFILE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 };
97 static const char hex[16] = "0123456789ABCDEF";
99 /***********************************************************************
100 * PROFILE_CopyEntry
102 * Copy the content of an entry into a buffer, removing quotes, and possibly
103 * translating environment variables.
105 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
106 BOOL strip_quote )
108 WCHAR quote = '\0';
110 if(!buffer) return;
112 if (strip_quote && ((*value == '\'') || (*value == '\"')))
114 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
117 lstrcpynW( buffer, value, len );
118 if (quote && (len >= lstrlenW(value))) buffer[strlenW(buffer)-1] = '\0';
121 /* byte-swaps shorts in-place in a buffer. len is in WCHARs */
122 static inline void PROFILE_ByteSwapShortBuffer(WCHAR * buffer, int len)
124 int i;
125 USHORT * shortbuffer = buffer;
126 for (i = 0; i < len; i++)
127 shortbuffer[i] = RtlUshortByteSwap(shortbuffer[i]);
130 /* writes any necessary encoding marker to the file */
131 static inline void PROFILE_WriteMarker(HANDLE hFile, ENCODING encoding)
133 DWORD dwBytesWritten;
134 WCHAR bom;
135 switch (encoding)
137 case ENCODING_ANSI:
138 break;
139 case ENCODING_UTF8:
140 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
141 break;
142 case ENCODING_UTF16LE:
143 bom = 0xFEFF;
144 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
145 break;
146 case ENCODING_UTF16BE:
147 bom = 0xFFFE;
148 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
149 break;
153 static void PROFILE_WriteLine( HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
155 char * write_buffer;
156 int write_buffer_len;
157 DWORD dwBytesWritten;
159 TRACE("writing: %s\n", debugstr_wn(szLine, len));
161 switch (encoding)
163 case ENCODING_ANSI:
164 write_buffer_len = WideCharToMultiByte(CP_ACP, 0, szLine, len, NULL, 0, NULL, NULL);
165 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
166 if (!write_buffer) return;
167 len = WideCharToMultiByte(CP_ACP, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
168 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
169 HeapFree(GetProcessHeap(), 0, write_buffer);
170 break;
171 case ENCODING_UTF8:
172 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
173 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
174 if (!write_buffer) return;
175 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
176 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
177 HeapFree(GetProcessHeap(), 0, write_buffer);
178 break;
179 case ENCODING_UTF16LE:
180 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
181 break;
182 case ENCODING_UTF16BE:
183 PROFILE_ByteSwapShortBuffer(szLine, len);
184 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
185 break;
186 default:
187 FIXME("encoding type %d not implemented\n", encoding);
191 /***********************************************************************
192 * PROFILE_Save
194 * Save a profile tree to a file.
196 static void PROFILE_Save( HANDLE hFile, const PROFILESECTION *section, ENCODING encoding )
198 PROFILEKEY *key;
199 WCHAR *buffer, *p;
201 PROFILE_WriteMarker(hFile, encoding);
203 for ( ; section; section = section->next)
205 int len = 0;
207 if (section->name[0]) len += strlenW(section->name) + 4;
209 for (key = section->key; key; key = key->next)
211 len += strlenW(key->name) + 2;
212 if (key->value) len += strlenW(key->value) + 1;
215 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
216 if (!buffer) return;
218 p = buffer;
219 if (section->name[0])
221 *p++ = '[';
222 strcpyW( p, section->name );
223 p += strlenW(p);
224 *p++ = ']';
225 *p++ = '\r';
226 *p++ = '\n';
229 for (key = section->key; key; key = key->next)
231 strcpyW( p, key->name );
232 p += strlenW(p);
233 if (key->value)
235 *p++ = '=';
236 strcpyW( p, key->value );
237 p += strlenW(p);
239 *p++ = '\r';
240 *p++ = '\n';
242 PROFILE_WriteLine( hFile, buffer, len, encoding );
243 HeapFree(GetProcessHeap(), 0, buffer);
248 /***********************************************************************
249 * PROFILE_Free
251 * Free a profile tree.
253 static void PROFILE_Free( PROFILESECTION *section )
255 PROFILESECTION *next_section;
256 PROFILEKEY *key, *next_key;
258 for ( ; section; section = next_section)
260 for (key = section->key; key; key = next_key)
262 next_key = key->next;
263 HeapFree( GetProcessHeap(), 0, key->value );
264 HeapFree( GetProcessHeap(), 0, key );
266 next_section = section->next;
267 HeapFree( GetProcessHeap(), 0, section );
271 /* returns 1 if a character white space else 0 */
272 static inline int PROFILE_isspaceW(WCHAR c)
274 /* ^Z (DOS EOF) is a space too (found on CD-ROMs) */
275 return isspaceW(c) || c == 0x1a;
278 static inline ENCODING PROFILE_DetectTextEncoding(const void * buffer, int * len)
280 int flags = IS_TEXT_UNICODE_SIGNATURE |
281 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
282 IS_TEXT_UNICODE_ODD_LENGTH;
283 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
285 *len = sizeof(bom_utf8);
286 return ENCODING_UTF8;
288 RtlIsTextUnicode(buffer, *len, &flags);
289 if (flags & IS_TEXT_UNICODE_SIGNATURE)
291 *len = sizeof(WCHAR);
292 return ENCODING_UTF16LE;
294 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
296 *len = sizeof(WCHAR);
297 return ENCODING_UTF16BE;
299 *len = 0;
300 return ENCODING_ANSI;
304 /***********************************************************************
305 * PROFILE_Load
307 * Load a profile tree from a file.
309 static PROFILESECTION *PROFILE_Load(HANDLE hFile, ENCODING * pEncoding)
311 void *buffer_base, *pBuffer;
312 WCHAR * szFile;
313 const WCHAR *szLineStart, *szLineEnd;
314 const WCHAR *szValueStart, *szEnd, *next_line;
315 int line = 0, len;
316 PROFILESECTION *section, *first_section;
317 PROFILESECTION **next_section;
318 PROFILEKEY *key, *prev_key, **next_key;
319 DWORD dwFileSize;
321 TRACE("%p\n", hFile);
323 dwFileSize = GetFileSize(hFile, NULL);
324 if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
325 return NULL;
327 buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
328 if (!buffer_base) return NULL;
330 if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
332 HeapFree(GetProcessHeap(), 0, buffer_base);
333 WARN("Error %d reading file\n", GetLastError());
334 return NULL;
336 len = dwFileSize;
337 *pEncoding = PROFILE_DetectTextEncoding(buffer_base, &len);
338 /* len is set to the number of bytes in the character marker.
339 * we want to skip these bytes */
340 pBuffer = (char *)buffer_base + len;
341 dwFileSize -= len;
342 switch (*pEncoding)
344 case ENCODING_ANSI:
345 TRACE("ANSI encoding\n");
347 len = MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, NULL, 0);
348 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
349 if (!szFile)
351 HeapFree(GetProcessHeap(), 0, buffer_base);
352 return NULL;
354 MultiByteToWideChar(CP_ACP, 0, pBuffer, dwFileSize, szFile, len);
355 szEnd = szFile + len;
356 break;
357 case ENCODING_UTF8:
358 TRACE("UTF8 encoding\n");
360 len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
361 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
362 if (!szFile)
364 HeapFree(GetProcessHeap(), 0, buffer_base);
365 return NULL;
367 MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
368 szEnd = szFile + len;
369 break;
370 case ENCODING_UTF16LE:
371 TRACE("UTF16 Little Endian encoding\n");
372 szFile = pBuffer;
373 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
374 break;
375 case ENCODING_UTF16BE:
376 TRACE("UTF16 Big Endian encoding\n");
377 szFile = pBuffer;
378 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
379 PROFILE_ByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
380 break;
381 default:
382 FIXME("encoding type %d not implemented\n", *pEncoding);
383 HeapFree(GetProcessHeap(), 0, buffer_base);
384 return NULL;
387 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
388 if(first_section == NULL)
390 if (szFile != pBuffer)
391 HeapFree(GetProcessHeap(), 0, szFile);
392 HeapFree(GetProcessHeap(), 0, buffer_base);
393 return NULL;
395 first_section->name[0] = 0;
396 first_section->key = NULL;
397 first_section->next = NULL;
398 next_section = &first_section->next;
399 next_key = &first_section->key;
400 prev_key = NULL;
401 next_line = szFile;
403 while (next_line < szEnd)
405 szLineStart = next_line;
406 next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
407 if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart);
408 if (!next_line) next_line = szEnd;
409 else next_line++;
410 szLineEnd = next_line;
412 line++;
414 /* get rid of white space */
415 while (szLineStart < szLineEnd && PROFILE_isspaceW(*szLineStart)) szLineStart++;
416 while ((szLineEnd > szLineStart) && PROFILE_isspaceW(szLineEnd[-1])) szLineEnd--;
418 if (szLineStart >= szLineEnd) continue;
420 if (*szLineStart == '[') /* section start */
422 const WCHAR * szSectionEnd;
423 if (!(szSectionEnd = memrchrW( szLineStart, ']', szLineEnd - szLineStart )))
425 WARN("Invalid section header at line %d: %s\n",
426 line, debugstr_wn(szLineStart, (int)(szLineEnd - szLineStart)) );
428 else
430 szLineStart++;
431 len = (int)(szSectionEnd - szLineStart);
432 /* no need to allocate +1 for NULL terminating character as
433 * already included in structure */
434 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
435 break;
436 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
437 section->name[len] = '\0';
438 section->key = NULL;
439 section->next = NULL;
440 *next_section = section;
441 next_section = &section->next;
442 next_key = &section->key;
443 prev_key = NULL;
445 TRACE("New section: %s\n", debugstr_w(section->name));
447 continue;
451 /* get rid of white space after the name and before the start
452 * of the value */
453 len = szLineEnd - szLineStart;
454 if ((szValueStart = memchrW( szLineStart, '=', szLineEnd - szLineStart )) != NULL)
456 const WCHAR *szNameEnd = szValueStart;
457 while ((szNameEnd > szLineStart) && PROFILE_isspaceW(szNameEnd[-1])) szNameEnd--;
458 len = szNameEnd - szLineStart;
459 szValueStart++;
460 while (szValueStart < szLineEnd && PROFILE_isspaceW(*szValueStart)) szValueStart++;
463 if (len || !prev_key || *prev_key->name)
465 /* no need to allocate +1 for NULL terminating character as
466 * already included in structure */
467 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
468 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
469 key->name[len] = '\0';
470 if (szValueStart)
472 len = (int)(szLineEnd - szValueStart);
473 key->value = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
474 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
475 key->value[len] = '\0';
477 else key->value = NULL;
479 key->next = NULL;
480 *next_key = key;
481 next_key = &key->next;
482 prev_key = key;
484 TRACE("New key: name=%s, value=%s\n",
485 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
488 if (szFile != pBuffer)
489 HeapFree(GetProcessHeap(), 0, szFile);
490 HeapFree(GetProcessHeap(), 0, buffer_base);
491 return first_section;
495 /***********************************************************************
496 * PROFILE_DeleteSection
498 * Delete a section from a profile tree.
500 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
502 while (*section)
504 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
506 PROFILESECTION *to_del = *section;
507 *section = to_del->next;
508 to_del->next = NULL;
509 PROFILE_Free( to_del );
510 return TRUE;
512 section = &(*section)->next;
514 return FALSE;
518 /***********************************************************************
519 * PROFILE_DeleteKey
521 * Delete a key from a profile tree.
523 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
524 LPCWSTR section_name, LPCWSTR key_name )
526 while (*section)
528 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
530 PROFILEKEY **key = &(*section)->key;
531 while (*key)
533 if (!strcmpiW( (*key)->name, key_name ))
535 PROFILEKEY *to_del = *key;
536 *key = to_del->next;
537 HeapFree( GetProcessHeap(), 0, to_del->value);
538 HeapFree( GetProcessHeap(), 0, to_del );
539 return TRUE;
541 key = &(*key)->next;
544 section = &(*section)->next;
546 return FALSE;
550 /***********************************************************************
551 * PROFILE_DeleteAllKeys
553 * Delete all keys from a profile tree.
555 static void PROFILE_DeleteAllKeys( LPCWSTR section_name)
557 PROFILESECTION **section= &CurProfile->section;
558 while (*section)
560 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
562 PROFILEKEY **key = &(*section)->key;
563 while (*key)
565 PROFILEKEY *to_del = *key;
566 *key = to_del->next;
567 HeapFree( GetProcessHeap(), 0, to_del->value);
568 HeapFree( GetProcessHeap(), 0, to_del );
569 CurProfile->changed =TRUE;
572 section = &(*section)->next;
577 /***********************************************************************
578 * PROFILE_Find
580 * Find a key in a profile tree, optionally creating it.
582 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
583 LPCWSTR key_name, BOOL create, BOOL create_always )
585 LPCWSTR p;
586 int seclen, keylen;
588 while (PROFILE_isspaceW(*section_name)) section_name++;
589 if (*section_name)
590 p = section_name + strlenW(section_name) - 1;
591 else
592 p = section_name;
594 while ((p > section_name) && PROFILE_isspaceW(*p)) p--;
595 seclen = p - section_name + 1;
597 while (PROFILE_isspaceW(*key_name)) key_name++;
598 if (*key_name)
599 p = key_name + strlenW(key_name) - 1;
600 else
601 p = key_name;
603 while ((p > key_name) && PROFILE_isspaceW(*p)) p--;
604 keylen = p - key_name + 1;
606 while (*section)
608 if ( ((*section)->name[0])
609 && (!(strncmpiW( (*section)->name, section_name, seclen )))
610 && (((*section)->name)[seclen] == '\0') )
612 PROFILEKEY **key = &(*section)->key;
614 while (*key)
616 /* If create_always is FALSE then we check if the keyname
617 * already exists. Otherwise we add it regardless of its
618 * existence, to allow keys to be added more than once in
619 * some cases.
621 if(!create_always)
623 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
624 && (((*key)->name)[keylen] == '\0') )
625 return *key;
627 key = &(*key)->next;
629 if (!create) return NULL;
630 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
631 return NULL;
632 strcpyW( (*key)->name, key_name );
633 (*key)->value = NULL;
634 (*key)->next = NULL;
635 return *key;
637 section = &(*section)->next;
639 if (!create) return NULL;
640 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
641 if(*section == NULL) return NULL;
642 strcpyW( (*section)->name, section_name );
643 (*section)->next = NULL;
644 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
645 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
647 HeapFree(GetProcessHeap(), 0, *section);
648 return NULL;
650 strcpyW( (*section)->key->name, key_name );
651 (*section)->key->value = NULL;
652 (*section)->key->next = NULL;
653 return (*section)->key;
657 /***********************************************************************
658 * PROFILE_FlushFile
660 * Flush the current profile to disk if changed.
662 static BOOL PROFILE_FlushFile(void)
664 HANDLE hFile = NULL;
665 FILETIME LastWriteTime;
667 if(!CurProfile)
669 WARN("No current profile!\n");
670 return FALSE;
673 if (!CurProfile->changed) return TRUE;
675 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
676 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
678 if (hFile == INVALID_HANDLE_VALUE)
680 WARN("could not save profile file %s (error was %d)\n", debugstr_w(CurProfile->filename), GetLastError());
681 return FALSE;
684 TRACE("Saving %s\n", debugstr_w(CurProfile->filename));
685 PROFILE_Save( hFile, CurProfile->section, CurProfile->encoding );
686 if(GetFileTime(hFile, NULL, NULL, &LastWriteTime))
687 CurProfile->LastWriteTime=LastWriteTime;
688 CloseHandle( hFile );
689 CurProfile->changed = FALSE;
690 return TRUE;
694 /***********************************************************************
695 * PROFILE_ReleaseFile
697 * Flush the current profile to disk and remove it from the cache.
699 static void PROFILE_ReleaseFile(void)
701 PROFILE_FlushFile();
702 PROFILE_Free( CurProfile->section );
703 HeapFree( GetProcessHeap(), 0, CurProfile->filename );
704 CurProfile->changed = FALSE;
705 CurProfile->section = NULL;
706 CurProfile->filename = NULL;
707 CurProfile->encoding = ENCODING_ANSI;
708 ZeroMemory(&CurProfile->LastWriteTime, sizeof(CurProfile->LastWriteTime));
711 /***********************************************************************
713 * Compares a file time with the current time. If the file time is
714 * at least 2.1 seconds in the past, return true.
716 * Intended as cache safety measure: The time resolution on FAT is
717 * two seconds, so files that are not at least two seconds old might
718 * keep their time even on modification, so don't cache them.
720 static BOOL is_not_current(FILETIME * ft)
722 FILETIME Now;
723 LONGLONG ftll, nowll;
724 GetSystemTimeAsFileTime(&Now);
725 ftll = ((LONGLONG)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
726 nowll = ((LONGLONG)Now.dwHighDateTime << 32) + Now.dwLowDateTime;
727 TRACE("%08x;%08x\n",(unsigned)ftll+21000000,(unsigned)nowll);
728 return ftll + 21000000 < nowll;
731 /***********************************************************************
732 * PROFILE_Open
734 * Open a profile file, checking the cached file first.
736 static BOOL PROFILE_Open( LPCWSTR filename, BOOL write_access )
738 WCHAR windirW[MAX_PATH];
739 WCHAR buffer[MAX_PATH];
740 HANDLE hFile = INVALID_HANDLE_VALUE;
741 FILETIME LastWriteTime;
742 int i,j;
743 PROFILE *tempProfile;
745 ZeroMemory(&LastWriteTime, sizeof(LastWriteTime));
747 /* First time around */
749 if(!CurProfile)
750 for(i=0;i<N_CACHED_PROFILES;i++)
752 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
753 if(MRUProfile[i] == NULL) break;
754 MRUProfile[i]->changed=FALSE;
755 MRUProfile[i]->section=NULL;
756 MRUProfile[i]->filename=NULL;
757 MRUProfile[i]->encoding=ENCODING_ANSI;
758 ZeroMemory(&MRUProfile[i]->LastWriteTime, sizeof(FILETIME));
761 GetWindowsDirectoryW( windirW, MAX_PATH );
763 if (!filename)
764 filename = wininiW;
766 if ((RtlDetermineDosPathNameType_U(filename) == RELATIVE_PATH) &&
767 !strchrW(filename, '\\') && !strchrW(filename, '/'))
769 static const WCHAR wszSeparator[] = {'\\', 0};
770 strcpyW(buffer, windirW);
771 strcatW(buffer, wszSeparator);
772 strcatW(buffer, filename);
774 else
776 LPWSTR dummy;
777 GetFullPathNameW(filename, sizeof(buffer)/sizeof(buffer[0]), buffer, &dummy);
780 TRACE("path: %s\n", debugstr_w(buffer));
782 hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
783 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
784 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
786 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
788 WARN("Error %d opening file %s\n", GetLastError(), debugstr_w(buffer));
789 return FALSE;
792 for(i=0;i<N_CACHED_PROFILES;i++)
794 if ((MRUProfile[i]->filename && !strcmpiW( buffer, MRUProfile[i]->filename )))
796 TRACE("MRU Filename: %s, new filename: %s\n", debugstr_w(MRUProfile[i]->filename), debugstr_w(buffer));
797 if(i)
799 PROFILE_FlushFile();
800 tempProfile=MRUProfile[i];
801 for(j=i;j>0;j--)
802 MRUProfile[j]=MRUProfile[j-1];
803 CurProfile=tempProfile;
806 if (hFile != INVALID_HANDLE_VALUE)
808 GetFileTime(hFile, NULL, NULL, &LastWriteTime);
809 if (!memcmp( &CurProfile->LastWriteTime, &LastWriteTime, sizeof(FILETIME) ) &&
810 is_not_current(&LastWriteTime))
811 TRACE("(%s): already opened (mru=%d)\n",
812 debugstr_w(buffer), i);
813 else
815 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
816 debugstr_w(buffer), i);
817 PROFILE_Free(CurProfile->section);
818 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
819 CurProfile->LastWriteTime = LastWriteTime;
821 CloseHandle(hFile);
823 else TRACE("(%s): already opened, not yet created (mru=%d)\n",
824 debugstr_w(buffer), i);
825 return TRUE;
829 /* Flush the old current profile */
830 PROFILE_FlushFile();
832 /* Make the oldest profile the current one only in order to get rid of it */
833 if(i==N_CACHED_PROFILES)
835 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
836 for(i=N_CACHED_PROFILES-1;i>0;i--)
837 MRUProfile[i]=MRUProfile[i-1];
838 CurProfile=tempProfile;
840 if(CurProfile->filename) PROFILE_ReleaseFile();
842 /* OK, now that CurProfile is definitely free we assign it our new file */
843 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(buffer)+1) * sizeof(WCHAR) );
844 strcpyW( CurProfile->filename, buffer );
846 if (hFile != INVALID_HANDLE_VALUE)
848 CurProfile->section = PROFILE_Load(hFile, &CurProfile->encoding);
849 GetFileTime(hFile, NULL, NULL, &CurProfile->LastWriteTime);
850 CloseHandle(hFile);
852 else
854 /* Does not exist yet, we will create it in PROFILE_FlushFile */
855 WARN("profile file %s not found\n", debugstr_w(buffer) );
857 return TRUE;
861 /***********************************************************************
862 * PROFILE_GetSection
864 * Returns all keys of a section.
865 * If return_values is TRUE, also include the corresponding values.
867 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
868 LPWSTR buffer, UINT len, BOOL return_values, BOOL return_noequalkeys )
870 PROFILEKEY *key;
872 if(!buffer) return 0;
874 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
876 while (section)
878 if (section->name[0] && !strcmpiW( section->name, section_name ))
880 UINT oldlen = len;
881 for (key = section->key; key; key = key->next)
883 if (len <= 2) break;
884 if (!*key->name) continue; /* Skip empty lines */
885 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
886 if (!return_noequalkeys && !return_values && !key->value) continue; /* Skip lines w.o. '=' */
887 PROFILE_CopyEntry( buffer, key->name, len - 1, 0 );
888 len -= strlenW(buffer) + 1;
889 buffer += strlenW(buffer) + 1;
890 if (len < 2)
891 break;
892 if (return_values && key->value) {
893 buffer[-1] = '=';
894 PROFILE_CopyEntry ( buffer, key->value, len - 1, 0 );
895 len -= strlenW(buffer) + 1;
896 buffer += strlenW(buffer) + 1;
899 *buffer = '\0';
900 if (len <= 1)
901 /*If either lpszSection or lpszKey is NULL and the supplied
902 destination buffer is too small to hold all the strings,
903 the last string is truncated and followed by two null characters.
904 In this case, the return value is equal to cchReturnBuffer
905 minus two. */
907 buffer[-1] = '\0';
908 return oldlen - 2;
910 return oldlen - len;
912 section = section->next;
914 buffer[0] = buffer[1] = '\0';
915 return 0;
918 /* See GetPrivateProfileSectionNamesA for documentation */
919 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
921 LPWSTR buf;
922 UINT buflen,tmplen;
923 PROFILESECTION *section;
925 TRACE("(%p, %d)\n", buffer, len);
927 if (!buffer || !len)
928 return 0;
929 if (len==1) {
930 *buffer='\0';
931 return 0;
934 buflen=len-1;
935 buf=buffer;
936 section = CurProfile->section;
937 while ((section!=NULL)) {
938 if (section->name[0]) {
939 tmplen = strlenW(section->name)+1;
940 if (tmplen >= buflen) {
941 if (buflen > 0) {
942 memcpy(buf, section->name, (buflen-1) * sizeof(WCHAR));
943 buf += buflen-1;
944 *buf++='\0';
946 *buf='\0';
947 return len-2;
949 memcpy(buf, section->name, tmplen * sizeof(WCHAR));
950 buf += tmplen;
951 buflen -= tmplen;
953 section = section->next;
955 *buf='\0';
956 return buf-buffer;
960 /***********************************************************************
961 * PROFILE_GetString
963 * Get a profile string.
965 * Tests with GetPrivateProfileString16, W95a,
966 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
967 * section key_name def_val res buffer
968 * "set1" "1" "x" 43 [data]
969 * "set1" "1 " "x" 43 [data] (!)
970 * "set1" " 1 "' "x" 43 [data] (!)
971 * "set1" "" "x" 1 "x"
972 * "set1" "" "x " 1 "x" (!)
973 * "set1" "" " x " 3 " x" (!)
974 * "set1" NULL "x" 6 "1\02\03\0\0"
975 * "set1" "" "x" 1 "x"
976 * NULL "1" "x" 0 "" (!)
977 * "" "1" "x" 1 "x"
978 * NULL NULL "" 0 ""
982 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
983 LPCWSTR def_val, LPWSTR buffer, UINT len, BOOL win32 )
985 PROFILEKEY *key = NULL;
986 static const WCHAR empty_strW[] = { 0 };
988 if(!buffer || !len) return 0;
990 if (!def_val) def_val = empty_strW;
991 if (key_name)
993 if (!key_name[0])
995 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
996 return strlenW(buffer);
998 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
999 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
1000 len, TRUE );
1001 TRACE("(%s,%s,%s): returning %s\n",
1002 debugstr_w(section), debugstr_w(key_name),
1003 debugstr_w(def_val), debugstr_w(buffer) );
1004 return strlenW( buffer );
1006 /* no "else" here ! */
1007 if (section && section[0])
1009 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, !win32);
1010 if (!buffer[0]) /* no luck -> def_val */
1012 PROFILE_CopyEntry(buffer, def_val, len, TRUE);
1013 ret = strlenW(buffer);
1015 return ret;
1017 buffer[0] = '\0';
1018 return 0;
1022 /***********************************************************************
1023 * PROFILE_SetString
1025 * Set a profile string.
1027 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
1028 LPCWSTR value, BOOL create_always )
1030 if (!key_name) /* Delete a whole section */
1032 TRACE("(%s)\n", debugstr_w(section_name));
1033 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
1034 section_name );
1035 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1036 this is not an error on application's level.*/
1038 else if (!value) /* Delete a key */
1040 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
1041 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
1042 section_name, key_name );
1043 return TRUE; /* same error handling as above */
1045 else /* Set the key value */
1047 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1048 key_name, TRUE, create_always );
1049 TRACE("(%s,%s,%s):\n",
1050 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
1051 if (!key) return FALSE;
1053 /* strip the leading spaces. We can safely strip \n\r and
1054 * friends too, they should not happen here anyway. */
1055 while (PROFILE_isspaceW(*value)) value++;
1057 if (key->value)
1059 if (!strcmpW( key->value, value ))
1061 TRACE(" no change needed\n" );
1062 return TRUE; /* No change needed */
1064 TRACE(" replacing %s\n", debugstr_w(key->value) );
1065 HeapFree( GetProcessHeap(), 0, key->value );
1067 else TRACE(" creating key\n" );
1068 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
1069 strcpyW( key->value, value );
1070 CurProfile->changed = TRUE;
1072 return TRUE;
1076 /********************* API functions **********************************/
1079 /***********************************************************************
1080 * GetProfileIntA (KERNEL32.@)
1082 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1084 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1087 /***********************************************************************
1088 * GetProfileIntW (KERNEL32.@)
1090 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1092 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1096 * if win32, copy:
1097 * - Section names if 'section' is NULL
1098 * - Keys in a Section if 'entry' is NULL
1099 * (see MSDN doc for GetPrivateProfileString)
1101 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1102 LPCWSTR def_val, LPWSTR buffer,
1103 UINT len, LPCWSTR filename,
1104 BOOL win32 )
1106 int ret;
1107 LPWSTR defval_tmp = NULL;
1109 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1110 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1112 /* strip any trailing ' ' of def_val. */
1113 if (def_val)
1115 LPCWSTR p = def_val + strlenW(def_val) - 1;
1117 while (p > def_val && *p == ' ')
1118 p--;
1120 if (p >= def_val)
1122 int len = (int)(p - def_val) + 1;
1124 defval_tmp = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1125 memcpy(defval_tmp, def_val, len * sizeof(WCHAR));
1126 defval_tmp[len] = '\0';
1127 def_val = defval_tmp;
1131 RtlEnterCriticalSection( &PROFILE_CritSect );
1133 if (PROFILE_Open( filename, FALSE )) {
1134 if (win32 && (section == NULL))
1135 ret = PROFILE_GetSectionNames(buffer, len);
1136 else
1137 /* PROFILE_GetString can handle the 'entry == NULL' case */
1138 ret = PROFILE_GetString( section, entry, def_val, buffer, len, win32 );
1139 } else if (buffer && def_val) {
1140 lstrcpynW( buffer, def_val, len );
1141 ret = strlenW( buffer );
1143 else
1144 ret = 0;
1146 RtlLeaveCriticalSection( &PROFILE_CritSect );
1148 HeapFree(GetProcessHeap(), 0, defval_tmp);
1150 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1152 return ret;
1155 /***********************************************************************
1156 * GetPrivateProfileString (KERNEL.128)
1158 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1159 LPCSTR def_val, LPSTR buffer,
1160 UINT16 len, LPCSTR filename )
1162 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1163 LPWSTR bufferW;
1164 INT16 retW, ret = 0;
1166 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1167 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1168 else sectionW.Buffer = NULL;
1169 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1170 else entryW.Buffer = NULL;
1171 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1172 else def_valW.Buffer = NULL;
1173 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1174 else filenameW.Buffer = NULL;
1176 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1177 def_valW.Buffer, bufferW, len,
1178 filenameW.Buffer, FALSE );
1179 if (len)
1181 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1182 if (!ret)
1184 ret = len - 1;
1185 buffer[ret] = 0;
1187 else
1188 ret--; /* strip terminating 0 */
1191 RtlFreeUnicodeString(&sectionW);
1192 RtlFreeUnicodeString(&entryW);
1193 RtlFreeUnicodeString(&def_valW);
1194 RtlFreeUnicodeString(&filenameW);
1195 HeapFree(GetProcessHeap(), 0, bufferW);
1196 return ret;
1199 /***********************************************************************
1200 * GetPrivateProfileStringA (KERNEL32.@)
1202 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1203 LPCSTR def_val, LPSTR buffer,
1204 UINT len, LPCSTR filename )
1206 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1207 LPWSTR bufferW;
1208 INT retW, ret = 0;
1210 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1211 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1212 else sectionW.Buffer = NULL;
1213 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1214 else entryW.Buffer = NULL;
1215 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1216 else def_valW.Buffer = NULL;
1217 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1218 else filenameW.Buffer = NULL;
1220 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1221 def_valW.Buffer, bufferW, len,
1222 filenameW.Buffer);
1223 if (len)
1225 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1226 if (!ret)
1228 ret = len - 1;
1229 buffer[ret] = 0;
1231 else
1232 ret--; /* strip terminating 0 */
1235 RtlFreeUnicodeString(&sectionW);
1236 RtlFreeUnicodeString(&entryW);
1237 RtlFreeUnicodeString(&def_valW);
1238 RtlFreeUnicodeString(&filenameW);
1239 HeapFree(GetProcessHeap(), 0, bufferW);
1240 return ret;
1243 /***********************************************************************
1244 * GetPrivateProfileStringW (KERNEL32.@)
1246 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1247 LPCWSTR def_val, LPWSTR buffer,
1248 UINT len, LPCWSTR filename )
1250 TRACE("(%s, %s, %s, %p, %d, %s)\n", debugstr_w(section), debugstr_w(entry), debugstr_w(def_val), buffer, len, debugstr_w(filename));
1252 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1253 buffer, len, filename, TRUE );
1256 /***********************************************************************
1257 * GetProfileStringA (KERNEL32.@)
1259 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1260 LPSTR buffer, UINT len )
1262 return GetPrivateProfileStringA( section, entry, def_val,
1263 buffer, len, "win.ini" );
1266 /***********************************************************************
1267 * GetProfileStringW (KERNEL32.@)
1269 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1270 LPCWSTR def_val, LPWSTR buffer, UINT len )
1272 return GetPrivateProfileStringW( section, entry, def_val,
1273 buffer, len, wininiW );
1276 /***********************************************************************
1277 * WriteProfileStringA (KERNEL32.@)
1279 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1280 LPCSTR string )
1282 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1285 /***********************************************************************
1286 * WriteProfileStringW (KERNEL32.@)
1288 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1289 LPCWSTR string )
1291 return WritePrivateProfileStringW( section, entry, string, wininiW );
1295 /***********************************************************************
1296 * GetPrivateProfileIntW (KERNEL32.@)
1298 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1299 INT def_val, LPCWSTR filename )
1301 WCHAR buffer[30];
1302 UNICODE_STRING bufferW;
1303 INT len;
1304 ULONG result;
1306 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1307 buffer, sizeof(buffer)/sizeof(WCHAR),
1308 filename )))
1309 return def_val;
1311 /* FIXME: if entry can be found but it's empty, then Win16 is
1312 * supposed to return 0 instead of def_val ! Difficult/problematic
1313 * to implement (every other failure also returns zero buffer),
1314 * thus wait until testing framework avail for making sure nothing
1315 * else gets broken that way. */
1316 if (!buffer[0]) return (UINT)def_val;
1318 RtlInitUnicodeString( &bufferW, buffer );
1319 RtlUnicodeStringToInteger( &bufferW, 0, &result);
1320 return result;
1323 /***********************************************************************
1324 * GetPrivateProfileIntA (KERNEL32.@)
1326 * FIXME: rewrite using unicode
1328 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1329 INT def_val, LPCSTR filename )
1331 UNICODE_STRING entryW, filenameW, sectionW;
1332 UINT res;
1333 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1334 else entryW.Buffer = NULL;
1335 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1336 else filenameW.Buffer = NULL;
1337 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1338 else sectionW.Buffer = NULL;
1339 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1340 filenameW.Buffer);
1341 RtlFreeUnicodeString(&sectionW);
1342 RtlFreeUnicodeString(&filenameW);
1343 RtlFreeUnicodeString(&entryW);
1344 return res;
1347 /***********************************************************************
1348 * GetPrivateProfileSectionW (KERNEL32.@)
1350 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1351 DWORD len, LPCWSTR filename )
1353 int ret = 0;
1355 if (!section || !buffer)
1357 SetLastError(ERROR_INVALID_PARAMETER);
1358 return 0;
1361 TRACE("(%s, %p, %d, %s)\n", debugstr_w(section), buffer, len, debugstr_w(filename));
1363 RtlEnterCriticalSection( &PROFILE_CritSect );
1365 if (PROFILE_Open( filename, FALSE ))
1366 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, TRUE, FALSE);
1368 RtlLeaveCriticalSection( &PROFILE_CritSect );
1370 return ret;
1373 /***********************************************************************
1374 * GetPrivateProfileSectionA (KERNEL32.@)
1376 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1377 DWORD len, LPCSTR filename )
1379 UNICODE_STRING sectionW, filenameW;
1380 LPWSTR bufferW;
1381 INT retW, ret = 0;
1383 if (!section || !buffer)
1385 SetLastError(ERROR_INVALID_PARAMETER);
1386 return 0;
1389 bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1390 RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1391 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1392 else filenameW.Buffer = NULL;
1394 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1395 if (len > 2)
1397 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1398 if (ret > 2)
1399 ret -= 1;
1400 else
1402 ret = 0;
1403 buffer[len-2] = 0;
1404 buffer[len-1] = 0;
1407 else
1409 buffer[0] = 0;
1410 buffer[1] = 0;
1413 RtlFreeUnicodeString(&sectionW);
1414 RtlFreeUnicodeString(&filenameW);
1415 HeapFree(GetProcessHeap(), 0, bufferW);
1416 return ret;
1419 /***********************************************************************
1420 * GetProfileSectionA (KERNEL32.@)
1422 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1424 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1427 /***********************************************************************
1428 * GetProfileSectionW (KERNEL32.@)
1430 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1432 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1436 /***********************************************************************
1437 * WritePrivateProfileStringW (KERNEL32.@)
1439 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1440 LPCWSTR string, LPCWSTR filename )
1442 BOOL ret = FALSE;
1444 RtlEnterCriticalSection( &PROFILE_CritSect );
1446 if (!section && !entry && !string) /* documented "file flush" case */
1448 if (!filename || PROFILE_Open( filename, TRUE ))
1450 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1453 else if (PROFILE_Open( filename, TRUE ))
1455 if (!section) {
1456 SetLastError(ERROR_FILE_NOT_FOUND);
1457 } else {
1458 ret = PROFILE_SetString( section, entry, string, FALSE);
1459 PROFILE_FlushFile();
1463 RtlLeaveCriticalSection( &PROFILE_CritSect );
1464 return ret;
1467 /***********************************************************************
1468 * WritePrivateProfileStringA (KERNEL32.@)
1470 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1471 LPCSTR string, LPCSTR filename )
1473 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1474 BOOL ret;
1476 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1477 else sectionW.Buffer = NULL;
1478 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1479 else entryW.Buffer = NULL;
1480 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1481 else stringW.Buffer = NULL;
1482 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1483 else filenameW.Buffer = NULL;
1485 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1486 stringW.Buffer, filenameW.Buffer);
1487 RtlFreeUnicodeString(&sectionW);
1488 RtlFreeUnicodeString(&entryW);
1489 RtlFreeUnicodeString(&stringW);
1490 RtlFreeUnicodeString(&filenameW);
1491 return ret;
1494 /***********************************************************************
1495 * WritePrivateProfileSectionW (KERNEL32.@)
1497 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1498 LPCWSTR string, LPCWSTR filename )
1500 BOOL ret = FALSE;
1501 LPWSTR p;
1503 RtlEnterCriticalSection( &PROFILE_CritSect );
1505 if (!section && !string)
1507 if (!filename || PROFILE_Open( filename, TRUE ))
1509 if (CurProfile) PROFILE_ReleaseFile(); /* always return FALSE in this case */
1512 else if (PROFILE_Open( filename, TRUE )) {
1513 if (!string) {/* delete the named section*/
1514 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1515 PROFILE_FlushFile();
1516 } else {
1517 PROFILE_DeleteAllKeys(section);
1518 ret = TRUE;
1519 while(*string) {
1520 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1521 strcpyW( buf, string );
1522 if((p = strchrW( buf, '='))) {
1523 *p='\0';
1524 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1526 HeapFree( GetProcessHeap(), 0, buf );
1527 string += strlenW(string)+1;
1529 PROFILE_FlushFile();
1533 RtlLeaveCriticalSection( &PROFILE_CritSect );
1534 return ret;
1537 /***********************************************************************
1538 * WritePrivateProfileSectionA (KERNEL32.@)
1540 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1541 LPCSTR string, LPCSTR filename)
1544 UNICODE_STRING sectionW, filenameW;
1545 LPWSTR stringW;
1546 BOOL ret;
1548 if (string)
1550 INT lenA, lenW;
1551 LPCSTR p = string;
1553 while(*p) p += strlen(p) + 1;
1554 lenA = p - string + 1;
1555 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1556 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1557 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1559 else stringW = NULL;
1560 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1561 else sectionW.Buffer = NULL;
1562 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1563 else filenameW.Buffer = NULL;
1565 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1567 HeapFree(GetProcessHeap(), 0, stringW);
1568 RtlFreeUnicodeString(&sectionW);
1569 RtlFreeUnicodeString(&filenameW);
1570 return ret;
1573 /***********************************************************************
1574 * WriteProfileSectionA (KERNEL32.@)
1576 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1579 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1582 /***********************************************************************
1583 * WriteProfileSectionW (KERNEL32.@)
1585 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1587 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1591 /***********************************************************************
1592 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1594 * Returns the section names contained in the specified file.
1595 * FIXME: Where do we find this file when the path is relative?
1596 * The section names are returned as a list of strings with an extra
1597 * '\0' to mark the end of the list. Except for that the behavior
1598 * depends on the Windows version.
1600 * Win95:
1601 * - if the buffer is 0 or 1 character long then it is as if it was of
1602 * infinite length.
1603 * - otherwise, if the buffer is too small only the section names that fit
1604 * are returned.
1605 * - note that this means if the buffer was too small to return even just
1606 * the first section name then a single '\0' will be returned.
1607 * - the return value is the number of characters written in the buffer,
1608 * except if the buffer was too small in which case len-2 is returned
1610 * Win2000:
1611 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1612 * '\0' and the return value is 0
1613 * - otherwise if the buffer is too small then the first section name that
1614 * does not fit is truncated so that the string list can be terminated
1615 * correctly (double '\0')
1616 * - the return value is the number of characters written in the buffer
1617 * except for the trailing '\0'. If the buffer is too small, then the
1618 * return value is len-2
1619 * - Win2000 has a bug that triggers when the section names and the
1620 * trailing '\0' fit exactly in the buffer. In that case the trailing
1621 * '\0' is missing.
1623 * Wine implements the observed Win2000 behavior (except for the bug).
1625 * Note that when the buffer is big enough then the return value may be any
1626 * value between 1 and len-1 (or len in Win95), including len-2.
1628 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1629 LPCWSTR filename)
1631 DWORD ret = 0;
1633 RtlEnterCriticalSection( &PROFILE_CritSect );
1635 if (PROFILE_Open( filename, FALSE ))
1636 ret = PROFILE_GetSectionNames(buffer, size);
1638 RtlLeaveCriticalSection( &PROFILE_CritSect );
1640 return ret;
1644 /***********************************************************************
1645 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1647 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1648 LPCSTR filename)
1650 UNICODE_STRING filenameW;
1651 LPWSTR bufferW;
1652 INT retW, ret = 0;
1654 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1655 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1656 else filenameW.Buffer = NULL;
1658 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1659 if (retW && size)
1661 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW+1, buffer, size-1, NULL, NULL);
1662 if (!ret)
1664 ret = size-2;
1665 buffer[size-1] = 0;
1667 else
1668 ret = ret-1;
1670 else if(size)
1671 buffer[0] = '\0';
1673 RtlFreeUnicodeString(&filenameW);
1674 HeapFree(GetProcessHeap(), 0, bufferW);
1675 return ret;
1678 /***********************************************************************
1679 * GetPrivateProfileStructW (KERNEL32.@)
1681 * Should match Win95's behaviour pretty much
1683 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1684 LPVOID buf, UINT len, LPCWSTR filename)
1686 BOOL ret = FALSE;
1688 RtlEnterCriticalSection( &PROFILE_CritSect );
1690 if (PROFILE_Open( filename, FALSE )) {
1691 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1692 if (k) {
1693 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1694 if (((strlenW(k->value) - 2) / 2) == len)
1696 LPWSTR end, p;
1697 BOOL valid = TRUE;
1698 WCHAR c;
1699 DWORD chksum = 0;
1701 end = k->value + strlenW(k->value); /* -> '\0' */
1702 /* check for invalid chars in ASCII coded hex string */
1703 for (p=k->value; p < end; p++)
1705 if (!isxdigitW(*p))
1707 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1708 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1709 valid = FALSE;
1710 break;
1713 if (valid)
1715 BOOL highnibble = TRUE;
1716 BYTE b = 0, val;
1717 LPBYTE binbuf = buf;
1719 end -= 2; /* don't include checksum in output data */
1720 /* translate ASCII hex format into binary data */
1721 for (p=k->value; p < end; p++)
1723 c = toupperW(*p);
1724 val = (c > '9') ?
1725 (c - 'A' + 10) : (c - '0');
1727 if (highnibble)
1728 b = val << 4;
1729 else
1731 b += val;
1732 *binbuf++ = b; /* feed binary data into output */
1733 chksum += b; /* calculate checksum */
1735 highnibble ^= 1; /* toggle */
1737 /* retrieve stored checksum value */
1738 c = toupperW(*p++);
1739 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1740 c = toupperW(*p);
1741 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1742 if (b == (chksum & 0xff)) /* checksums match ? */
1743 ret = TRUE;
1748 RtlLeaveCriticalSection( &PROFILE_CritSect );
1750 return ret;
1753 /***********************************************************************
1754 * GetPrivateProfileStructA (KERNEL32.@)
1756 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1757 LPVOID buffer, UINT len, LPCSTR filename)
1759 UNICODE_STRING sectionW, keyW, filenameW;
1760 INT ret;
1762 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1763 else sectionW.Buffer = NULL;
1764 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1765 else keyW.Buffer = NULL;
1766 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1767 else filenameW.Buffer = NULL;
1769 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1770 filenameW.Buffer);
1771 /* Do not translate binary data. */
1773 RtlFreeUnicodeString(&sectionW);
1774 RtlFreeUnicodeString(&keyW);
1775 RtlFreeUnicodeString(&filenameW);
1776 return ret;
1781 /***********************************************************************
1782 * WritePrivateProfileStructW (KERNEL32.@)
1784 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1785 LPVOID buf, UINT bufsize, LPCWSTR filename)
1787 BOOL ret = FALSE;
1788 LPBYTE binbuf;
1789 LPWSTR outstring, p;
1790 DWORD sum = 0;
1792 if (!section && !key && !buf) /* flush the cache */
1793 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1795 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1796 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1797 p = outstring;
1798 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1799 *p++ = hex[*binbuf >> 4];
1800 *p++ = hex[*binbuf & 0xf];
1801 sum += *binbuf;
1803 /* checksum is sum & 0xff */
1804 *p++ = hex[(sum & 0xf0) >> 4];
1805 *p++ = hex[sum & 0xf];
1806 *p++ = '\0';
1808 RtlEnterCriticalSection( &PROFILE_CritSect );
1810 if (PROFILE_Open( filename, TRUE )) {
1811 ret = PROFILE_SetString( section, key, outstring, FALSE);
1812 PROFILE_FlushFile();
1815 RtlLeaveCriticalSection( &PROFILE_CritSect );
1817 HeapFree( GetProcessHeap(), 0, outstring );
1819 return ret;
1822 /***********************************************************************
1823 * WritePrivateProfileStructA (KERNEL32.@)
1825 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1826 LPVOID buf, UINT bufsize, LPCSTR filename)
1828 UNICODE_STRING sectionW, keyW, filenameW;
1829 INT ret;
1831 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1832 else sectionW.Buffer = NULL;
1833 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1834 else keyW.Buffer = NULL;
1835 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1836 else filenameW.Buffer = NULL;
1838 /* Do not translate binary data. */
1839 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1840 filenameW.Buffer);
1842 RtlFreeUnicodeString(&sectionW);
1843 RtlFreeUnicodeString(&keyW);
1844 RtlFreeUnicodeString(&filenameW);
1845 return ret;
1849 /***********************************************************************
1850 * WriteOutProfiles (KERNEL.315)
1852 void WINAPI WriteOutProfiles16(void)
1854 RtlEnterCriticalSection( &PROFILE_CritSect );
1855 PROFILE_FlushFile();
1856 RtlLeaveCriticalSection( &PROFILE_CritSect );
1859 /***********************************************************************
1860 * OpenProfileUserMapping (KERNEL32.@)
1862 BOOL WINAPI OpenProfileUserMapping(void) {
1863 FIXME("(), stub!\n");
1864 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1865 return FALSE;
1868 /***********************************************************************
1869 * CloseProfileUserMapping (KERNEL32.@)
1871 BOOL WINAPI CloseProfileUserMapping(void) {
1872 FIXME("(), stub!\n");
1873 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1874 return FALSE;