Quiet some valgrind reports.
[wine/wine64.git] / files / profile.c
blobe13a7c7ad9b61f997618f75a674eb9c8cf47f5a1
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 <ctype.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winerror.h"
40 #include "winternl.h"
41 #include "wine/winbase16.h"
42 #include "drive.h"
43 #include "file.h"
44 #include "heap.h"
45 #include "wine/unicode.h"
46 #include "wine/server.h"
47 #include "wine/library.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(profile);
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 *dos_name;
72 char *unix_name;
73 WCHAR *filename;
74 time_t mtime;
75 } PROFILE;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR emptystringW[] = {0};
91 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
95 static const char hex[16] = "0123456789ABCDEF";
97 /***********************************************************************
98 * PROFILE_CopyEntry
100 * Copy the content of an entry into a buffer, removing quotes, and possibly
101 * translating environment variables.
103 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
104 int handle_env, BOOL strip_quote )
106 WCHAR quote = '\0';
107 LPCWSTR p;
109 if(!buffer) return;
111 if (strip_quote && ((*value == '\'') || (*value == '\"')))
113 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
116 if (!handle_env)
118 lstrcpynW( buffer, value, len );
119 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
120 return;
123 p = value;
124 while (*p && (len > 1))
126 if ((*p == '$') && (p[1] == '{'))
128 WCHAR env_val[1024];
129 LPCWSTR p2 = strchrW( p, '}' );
130 int copy_len;
131 if (!p2) continue; /* ignore it */
132 copy_len = min( 1024, (int)(p2-p)-1 );
133 strncpyW(env_val, p + 2, copy_len );
134 env_val[copy_len - 1] = 0; /* ensure 0 termination */
135 *buffer = 0;
136 if (GetEnvironmentVariableW( env_val, buffer, len))
138 copy_len = strlenW( buffer );
139 buffer += copy_len;
140 len -= copy_len;
142 p = p2 + 1;
144 else
146 *buffer++ = *p++;
147 len--;
150 if (quote && (len > 1)) buffer--;
151 *buffer = '\0';
155 /***********************************************************************
156 * PROFILE_Save
158 * Save a profile tree to a file.
160 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
162 PROFILEKEY *key;
163 char buffer[PROFILE_MAX_LINE_LEN];
165 for ( ; section; section = section->next)
167 if (section->name[0])
169 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
170 fprintf( file, "\r\n[%s]\r\n", buffer );
172 for (key = section->key; key; key = key->next)
174 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
175 fprintf( file, "%s", buffer );
176 if (key->value)
178 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
179 fprintf( file, "=%s", buffer );
181 fprintf( file, "\r\n" );
187 /***********************************************************************
188 * PROFILE_Free
190 * Free a profile tree.
192 static void PROFILE_Free( PROFILESECTION *section )
194 PROFILESECTION *next_section;
195 PROFILEKEY *key, *next_key;
197 for ( ; section; section = next_section)
199 for (key = section->key; key; key = next_key)
201 next_key = key->next;
202 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
203 HeapFree( GetProcessHeap(), 0, key );
205 next_section = section->next;
206 HeapFree( GetProcessHeap(), 0, section );
210 static inline int PROFILE_isspace(char c)
212 if (isspace(c)) return 1;
213 if (c=='\r' || c==0x1a) return 1;
214 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
215 return 0;
219 /***********************************************************************
220 * PROFILE_Load
222 * Load a profile tree from a file.
224 static PROFILESECTION *PROFILE_Load( FILE *file )
226 char buffer[PROFILE_MAX_LINE_LEN];
227 char *p, *p2;
228 int line = 0, len;
229 PROFILESECTION *section, *first_section;
230 PROFILESECTION **next_section;
231 PROFILEKEY *key, *prev_key, **next_key;
233 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
234 if(first_section == NULL) return NULL;
235 first_section->name[0] = 0;
236 first_section->key = NULL;
237 first_section->next = NULL;
238 next_section = &first_section->next;
239 next_key = &first_section->key;
240 prev_key = NULL;
242 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
244 line++;
245 p = buffer;
246 while (*p && PROFILE_isspace(*p)) p++;
247 if (*p == '[') /* section start */
249 if (!(p2 = strrchr( p, ']' )))
251 WARN("Invalid section header at line %d: '%s'\n",
252 line, p );
254 else
256 *p2 = '\0';
257 p++;
258 len = strlen(p);
259 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
260 break;
261 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
262 section->key = NULL;
263 section->next = NULL;
264 *next_section = section;
265 next_section = &section->next;
266 next_key = &section->key;
267 prev_key = NULL;
269 TRACE("New section: %s\n", debugstr_w(section->name));
271 continue;
275 p2=p+strlen(p) - 1;
276 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
278 if ((p2 = strchr( p, '=' )) != NULL)
280 char *p3 = p2 - 1;
281 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
282 *p2++ = '\0';
283 while (*p2 && PROFILE_isspace(*p2)) p2++;
286 if(*p || !prev_key || *prev_key->name)
288 len = strlen(p);
289 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
290 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
291 if (p2)
293 len = strlen(p2) + 1;
294 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
295 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
297 else key->value = NULL;
299 key->next = NULL;
300 *next_key = key;
301 next_key = &key->next;
302 prev_key = key;
304 TRACE("New key: name=%s, value=%s\n",
305 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
308 return first_section;
312 /***********************************************************************
313 * PROFILE_DeleteSection
315 * Delete a section from a profile tree.
317 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
319 while (*section)
321 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
323 PROFILESECTION *to_del = *section;
324 *section = to_del->next;
325 to_del->next = NULL;
326 PROFILE_Free( to_del );
327 return TRUE;
329 section = &(*section)->next;
331 return FALSE;
335 /***********************************************************************
336 * PROFILE_DeleteKey
338 * Delete a key from a profile tree.
340 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
341 LPCWSTR section_name, LPCWSTR key_name )
343 while (*section)
345 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
347 PROFILEKEY **key = &(*section)->key;
348 while (*key)
350 if (!strcmpiW( (*key)->name, key_name ))
352 PROFILEKEY *to_del = *key;
353 *key = to_del->next;
354 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
355 HeapFree( GetProcessHeap(), 0, to_del );
356 return TRUE;
358 key = &(*key)->next;
361 section = &(*section)->next;
363 return FALSE;
367 /***********************************************************************
368 * PROFILE_DeleteAllKeys
370 * Delete all keys from a profile tree.
372 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
374 PROFILESECTION **section= &CurProfile->section;
375 while (*section)
377 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
379 PROFILEKEY **key = &(*section)->key;
380 while (*key)
382 PROFILEKEY *to_del = *key;
383 *key = to_del->next;
384 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
385 HeapFree( GetProcessHeap(), 0, to_del );
386 CurProfile->changed =TRUE;
389 section = &(*section)->next;
394 /***********************************************************************
395 * PROFILE_Find
397 * Find a key in a profile tree, optionally creating it.
399 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
400 LPCWSTR key_name, BOOL create, BOOL create_always )
402 LPCWSTR p;
403 int seclen, keylen;
405 while (PROFILE_isspace(*section_name)) section_name++;
406 p = section_name + strlenW(section_name) - 1;
407 while ((p > section_name) && PROFILE_isspace(*p)) p--;
408 seclen = p - section_name + 1;
410 while (PROFILE_isspace(*key_name)) key_name++;
411 p = key_name + strlenW(key_name) - 1;
412 while ((p > key_name) && PROFILE_isspace(*p)) p--;
413 keylen = p - key_name + 1;
415 while (*section)
417 if ( ((*section)->name[0])
418 && (!(strncmpiW( (*section)->name, section_name, seclen )))
419 && (((*section)->name)[seclen] == '\0') )
421 PROFILEKEY **key = &(*section)->key;
423 while (*key)
425 /* If create_always is FALSE then we check if the keyname already exists.
426 * Otherwise we add it regardless of its existence, to allow
427 * keys to be added more then once in some cases.
429 if(!create_always)
431 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
432 && (((*key)->name)[keylen] == '\0') )
433 return *key;
435 key = &(*key)->next;
437 if (!create) return NULL;
438 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
439 return NULL;
440 strcpyW( (*key)->name, key_name );
441 (*key)->value = NULL;
442 (*key)->next = NULL;
443 return *key;
445 section = &(*section)->next;
447 if (!create) return NULL;
448 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
449 if(*section == NULL) return NULL;
450 strcpyW( (*section)->name, section_name );
451 (*section)->next = NULL;
452 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
453 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
455 HeapFree(GetProcessHeap(), 0, *section);
456 return NULL;
458 strcpyW( (*section)->key->name, key_name );
459 (*section)->key->value = NULL;
460 (*section)->key->next = NULL;
461 return (*section)->key;
465 /***********************************************************************
466 * PROFILE_FlushFile
468 * Flush the current profile to disk if changed.
470 static BOOL PROFILE_FlushFile(void)
472 char *p, buffer[MAX_PATHNAME_LEN];
473 const char *unix_name;
474 FILE *file = NULL;
475 struct stat buf;
477 if(!CurProfile)
479 WARN("No current profile!\n");
480 return FALSE;
483 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
484 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
486 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
487 WCHAR *name, *name_lwr;
488 /* Try to create it in $HOME/.wine */
489 /* FIXME: this will need a more general solution */
490 strcpy( buffer, wine_get_config_dir() );
491 p = buffer + strlen(buffer);
492 *p++ = '/';
493 *p = 0; /* make strlen() below happy */
494 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
496 /* create a lower cased version of the name */
497 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
498 strcpyW(name_lwr, name);
499 strlwrW(name_lwr);
500 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
501 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
502 HeapFree(GetProcessHeap(), 0, name_lwr);
504 file = fopen( buffer, "w" );
505 unix_name = buffer;
508 if (!file)
510 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
511 return FALSE;
514 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
515 PROFILE_Save( file, CurProfile->section );
516 fclose( file );
517 CurProfile->changed = FALSE;
518 if(!stat(unix_name,&buf))
519 CurProfile->mtime=buf.st_mtime;
520 return TRUE;
524 /***********************************************************************
525 * PROFILE_ReleaseFile
527 * Flush the current profile to disk and remove it from the cache.
529 static void PROFILE_ReleaseFile(void)
531 PROFILE_FlushFile();
532 PROFILE_Free( CurProfile->section );
533 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
534 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
535 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
536 CurProfile->changed = FALSE;
537 CurProfile->section = NULL;
538 CurProfile->dos_name = NULL;
539 CurProfile->unix_name = NULL;
540 CurProfile->filename = NULL;
541 CurProfile->mtime = 0;
545 /***********************************************************************
546 * PROFILE_Open
548 * Open a profile file, checking the cached file first.
550 static BOOL PROFILE_Open( LPCWSTR filename )
552 DOS_FULL_NAME full_name;
553 char buffer[MAX_PATHNAME_LEN];
554 WCHAR *newdos_name;
555 WCHAR *name, *name_lwr;
556 char *p;
557 FILE *file = NULL;
558 int i,j;
559 struct stat buf;
560 PROFILE *tempProfile;
562 /* First time around */
564 if(!CurProfile)
565 for(i=0;i<N_CACHED_PROFILES;i++)
567 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
568 if(MRUProfile[i] == NULL) break;
569 MRUProfile[i]->changed=FALSE;
570 MRUProfile[i]->section=NULL;
571 MRUProfile[i]->dos_name=NULL;
572 MRUProfile[i]->unix_name=NULL;
573 MRUProfile[i]->filename=NULL;
574 MRUProfile[i]->mtime=0;
577 /* Check for a match */
579 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
580 strchrW( filename, ':' ))
582 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
584 else
586 static const WCHAR bkslashW[] = {'\\',0};
587 WCHAR windirW[MAX_PATH];
589 GetWindowsDirectoryW( windirW, MAX_PATH );
590 strcatW( windirW, bkslashW );
591 strcatW( windirW, filename );
592 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
595 for(i=0;i<N_CACHED_PROFILES;i++)
597 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
598 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
600 if(i)
602 PROFILE_FlushFile();
603 tempProfile=MRUProfile[i];
604 for(j=i;j>0;j--)
605 MRUProfile[j]=MRUProfile[j-1];
606 CurProfile=tempProfile;
608 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
609 TRACE("(%s): already opened (mru=%d)\n",
610 debugstr_w(filename), i );
611 else
612 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
613 debugstr_w(filename), i );
614 return TRUE;
618 /* Flush the old current profile */
619 PROFILE_FlushFile();
621 /* Make the oldest profile the current one only in order to get rid of it */
622 if(i==N_CACHED_PROFILES)
624 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
625 for(i=N_CACHED_PROFILES-1;i>0;i--)
626 MRUProfile[i]=MRUProfile[i-1];
627 CurProfile=tempProfile;
629 if(CurProfile->filename) PROFILE_ReleaseFile();
631 /* OK, now that CurProfile is definitely free we assign it our new file */
632 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
633 strcpyW( newdos_name, full_name.short_name );
634 CurProfile->dos_name = newdos_name;
635 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
636 strcpyW( CurProfile->filename, filename );
638 /* Try to open the profile file, first in $HOME/.wine */
640 /* FIXME: this will need a more general solution */
641 strcpy( buffer, wine_get_config_dir() );
642 p = buffer + strlen(buffer);
643 *p++ = '/';
644 *p = 0; /* make strlen() below happy */
645 name = strrchrW( newdos_name, '\\' ) + 1;
647 /* create a lower cased version of the name */
648 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
649 strcpyW(name_lwr, name);
650 strlwrW(name_lwr);
651 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
652 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
653 HeapFree(GetProcessHeap(), 0, name_lwr);
655 if ((file = fopen( buffer, "r" )))
657 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
658 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
659 strcpy( CurProfile->unix_name, buffer );
661 else
663 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
664 strcpy( CurProfile->unix_name, full_name.long_name );
665 if ((file = fopen( full_name.long_name, "r" )))
666 TRACE("(%s): found it in %s\n",
667 debugstr_w(filename), full_name.long_name );
670 if (file)
672 CurProfile->section = PROFILE_Load( file );
673 fclose( file );
674 if(!stat(CurProfile->unix_name,&buf))
675 CurProfile->mtime=buf.st_mtime;
677 else
679 /* Does not exist yet, we will create it in PROFILE_FlushFile */
680 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
682 return TRUE;
686 /***********************************************************************
687 * PROFILE_GetSection
689 * Returns all keys of a section.
690 * If return_values is TRUE, also include the corresponding values.
692 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
693 LPWSTR buffer, UINT len, BOOL handle_env,
694 BOOL return_values )
696 PROFILEKEY *key;
698 if(!buffer) return 0;
700 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
702 while (section)
704 if (section->name[0] && !strcmpiW( section->name, section_name ))
706 UINT oldlen = len;
707 for (key = section->key; key; key = key->next)
709 if (len <= 2) break;
710 if (!*key->name) continue; /* Skip empty lines */
711 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
712 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
713 len -= strlenW(buffer) + 1;
714 buffer += strlenW(buffer) + 1;
715 if (len < 2)
716 break;
717 if (return_values && key->value) {
718 buffer[-1] = '=';
719 PROFILE_CopyEntry ( buffer,
720 key->value, len - 1, handle_env, 0 );
721 len -= strlenW(buffer) + 1;
722 buffer += strlenW(buffer) + 1;
725 *buffer = '\0';
726 if (len <= 1)
727 /*If either lpszSection or lpszKey is NULL and the supplied
728 destination buffer is too small to hold all the strings,
729 the last string is truncated and followed by two null characters.
730 In this case, the return value is equal to cchReturnBuffer
731 minus two. */
733 buffer[-1] = '\0';
734 return oldlen - 2;
736 return oldlen - len;
738 section = section->next;
740 buffer[0] = buffer[1] = '\0';
741 return 0;
744 /* See GetPrivateProfileSectionNamesA for documentation */
745 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
747 LPWSTR buf;
748 UINT f,l;
749 PROFILESECTION *section;
751 if (!buffer || !len)
752 return 0;
753 if (len==1) {
754 *buffer='\0';
755 return 0;
758 f=len-1;
759 buf=buffer;
760 section = CurProfile->section;
761 while ((section!=NULL)) {
762 if (section->name[0]) {
763 l = strlenW(section->name)+1;
764 if (l > f) {
765 if (f>0) {
766 strncpyW(buf, section->name, f-1);
767 buf += f-1;
768 *buf++='\0';
770 *buf='\0';
771 return len-2;
773 strcpyW(buf, section->name);
774 buf += l;
775 f -= l;
777 section = section->next;
779 *buf='\0';
780 return buf-buffer;
784 /***********************************************************************
785 * PROFILE_GetString
787 * Get a profile string.
789 * Tests with GetPrivateProfileString16, W95a,
790 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
791 * section key_name def_val res buffer
792 * "set1" "1" "x" 43 [data]
793 * "set1" "1 " "x" 43 [data] (!)
794 * "set1" " 1 "' "x" 43 [data] (!)
795 * "set1" "" "x" 1 "x"
796 * "set1" "" "x " 1 "x" (!)
797 * "set1" "" " x " 3 " x" (!)
798 * "set1" NULL "x" 6 "1\02\03\0\0"
799 * "set1" "" "x" 1 "x"
800 * NULL "1" "x" 0 "" (!)
801 * "" "1" "x" 1 "x"
802 * NULL NULL "" 0 ""
806 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
807 LPCWSTR def_val, LPWSTR buffer, UINT len )
809 PROFILEKEY *key = NULL;
810 static const WCHAR empty_strW[] = { 0 };
812 if(!buffer) return 0;
814 if (!def_val) def_val = empty_strW;
815 if (key_name)
817 if (!key_name[0])
819 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
820 return 0;
822 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
823 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
824 len, FALSE, TRUE );
825 TRACE("(%s,%s,%s): returning %s\n",
826 debugstr_w(section), debugstr_w(key_name),
827 debugstr_w(def_val), debugstr_w(buffer) );
828 return strlenW( buffer );
830 /* no "else" here ! */
831 if (section && section[0])
833 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
834 if (!buffer[0]) /* no luck -> def_val */
836 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
837 ret = strlenW(buffer);
839 return ret;
841 buffer[0] = '\0';
842 return 0;
846 /***********************************************************************
847 * PROFILE_SetString
849 * Set a profile string.
851 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
852 LPCWSTR value, BOOL create_always )
854 if (!key_name) /* Delete a whole section */
856 TRACE("(%s)\n", debugstr_w(section_name));
857 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
858 section_name );
859 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
860 this is not an error on application's level.*/
862 else if (!value) /* Delete a key */
864 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
865 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
866 section_name, key_name );
867 return TRUE; /* same error handling as above */
869 else /* Set the key value */
871 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
872 key_name, TRUE, create_always );
873 TRACE("(%s,%s,%s):\n",
874 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
875 if (!key) return FALSE;
876 if (key->value)
878 /* strip the leading spaces. We can safely strip \n\r and
879 * friends too, they should not happen here anyway. */
880 while (PROFILE_isspace(*value)) value++;
882 if (!strcmpW( key->value, value ))
884 TRACE(" no change needed\n" );
885 return TRUE; /* No change needed */
887 TRACE(" replacing %s\n", debugstr_w(key->value) );
888 HeapFree( GetProcessHeap(), 0, key->value );
890 else TRACE(" creating key\n" );
891 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
892 strcpyW( key->value, value );
893 CurProfile->changed = TRUE;
895 return TRUE;
899 /***********************************************************************
900 * get_profile_key
902 static HKEY get_profile_key(void)
904 static HKEY profile_key;
906 if (!profile_key)
908 OBJECT_ATTRIBUTES attr;
909 UNICODE_STRING nameW;
910 HKEY hkey;
912 attr.Length = sizeof(attr);
913 attr.RootDirectory = 0;
914 attr.ObjectName = &nameW;
915 attr.Attributes = 0;
916 attr.SecurityDescriptor = NULL;
917 attr.SecurityQualityOfService = NULL;
919 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
920 NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL ))
922 ERR("Cannot create config registry key\n" );
923 ExitProcess( 1 );
925 RtlFreeUnicodeString( &nameW );
927 if (InterlockedCompareExchangePointer( (void **)&profile_key, hkey, 0 ))
928 NtClose( hkey ); /* somebody beat us to it */
930 return profile_key;
934 /***********************************************************************
935 * PROFILE_GetWineIniString
937 * Get a config string from the wine.ini file.
939 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
940 LPCWSTR def, LPWSTR buffer, int len )
942 HKEY hkey;
943 NTSTATUS err;
944 OBJECT_ATTRIBUTES attr;
945 UNICODE_STRING nameW;
947 attr.Length = sizeof(attr);
948 attr.RootDirectory = get_profile_key();
949 attr.ObjectName = &nameW;
950 attr.Attributes = 0;
951 attr.SecurityDescriptor = NULL;
952 attr.SecurityQualityOfService = NULL;
953 RtlInitUnicodeString( &nameW, section );
954 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
956 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
957 DWORD count;
959 RtlInitUnicodeString( &nameW, key_name );
960 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
961 tmp, sizeof(tmp), &count )))
963 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
964 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
966 NtClose( hkey );
969 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
970 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
971 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
972 return strlenW(buffer);
976 /******************************************************************************
978 * PROFILE_GetWineIniBool
980 * Reads a boolean value from the wine.ini file. This function attempts to
981 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
982 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
983 * true. Anything else results in the return of the default value.
985 * This function uses 1 to indicate true, and 0 for false. You can check
986 * for existence by setting def to something other than 0 or 1 and
987 * examining the return value.
989 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
991 static const WCHAR def_valueW[] = {'~',0};
992 WCHAR key_value[2];
993 int retval;
995 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
997 switch(key_value[0]) {
998 case 'n':
999 case 'N':
1000 case 'f':
1001 case 'F':
1002 case '0':
1003 retval = 0;
1004 break;
1006 case 'y':
1007 case 'Y':
1008 case 't':
1009 case 'T':
1010 case '1':
1011 retval = 1;
1012 break;
1014 default:
1015 retval = def;
1018 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1019 def ? "TRUE" : "FALSE", key_value[0],
1020 retval ? "TRUE" : "FALSE");
1022 return retval;
1026 /***********************************************************************
1027 * PROFILE_UsageWineIni
1029 * Explain the wine.ini file to those who don't read documentation.
1030 * Keep below one screenful in length so that error messages above are
1031 * noticed.
1033 void PROFILE_UsageWineIni(void)
1035 MESSAGE("Perhaps you have not properly edited or created "
1036 "your Wine configuration file,\n");
1037 MESSAGE("which is (supposed to be) '%s/config'.\n", wine_get_config_dir());
1041 /********************* API functions **********************************/
1043 /***********************************************************************
1044 * GetProfileInt (KERNEL.57)
1046 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1048 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1052 /***********************************************************************
1053 * GetProfileIntA (KERNEL32.@)
1055 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1057 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1060 /***********************************************************************
1061 * GetProfileIntW (KERNEL32.@)
1063 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1065 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1069 * if allow_section_name_copy is TRUE, allow the copying :
1070 * - of Section names if 'section' is NULL
1071 * - of Keys in a Section if 'entry' is NULL
1072 * (see MSDN doc for GetPrivateProfileString)
1074 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1075 LPCWSTR def_val, LPWSTR buffer,
1076 UINT len, LPCWSTR filename,
1077 BOOL allow_section_name_copy )
1079 int ret;
1080 LPWSTR pDefVal = NULL;
1082 if (!filename)
1083 filename = wininiW;
1085 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1086 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1088 /* strip any trailing ' ' of def_val. */
1089 if (def_val)
1091 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1093 while (p > def_val)
1095 p--;
1096 if ((*p) != ' ')
1097 break;
1099 if (*p == ' ') /* ouch, contained trailing ' ' */
1101 int len = (int)(p - def_val);
1102 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1103 strncpyW(pDefVal, def_val, len);
1104 pDefVal[len] = '\0';
1107 if (!pDefVal)
1108 pDefVal = (LPWSTR)def_val;
1110 EnterCriticalSection( &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 LeaveCriticalSection( &PROFILE_CritSect );
1125 if (pDefVal != def_val) /* allocated */
1126 HeapFree(GetProcessHeap(), 0, 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 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1229 buffer, len, filename, TRUE );
1232 /***********************************************************************
1233 * GetProfileString (KERNEL.58)
1235 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1236 LPSTR buffer, UINT16 len )
1238 return GetPrivateProfileString16( section, entry, def_val,
1239 buffer, len, "win.ini" );
1242 /***********************************************************************
1243 * GetProfileStringA (KERNEL32.@)
1245 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1246 LPSTR buffer, UINT len )
1248 return GetPrivateProfileStringA( section, entry, def_val,
1249 buffer, len, "win.ini" );
1252 /***********************************************************************
1253 * GetProfileStringW (KERNEL32.@)
1255 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1256 LPCWSTR def_val, LPWSTR buffer, UINT len )
1258 return GetPrivateProfileStringW( section, entry, def_val,
1259 buffer, len, wininiW );
1262 /***********************************************************************
1263 * WriteProfileString (KERNEL.59)
1265 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1266 LPCSTR string )
1268 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1271 /***********************************************************************
1272 * WriteProfileStringA (KERNEL32.@)
1274 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1275 LPCSTR string )
1277 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1280 /***********************************************************************
1281 * WriteProfileStringW (KERNEL32.@)
1283 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1284 LPCWSTR string )
1286 return WritePrivateProfileStringW( section, entry, string, wininiW );
1290 /***********************************************************************
1291 * GetPrivateProfileInt (KERNEL.127)
1293 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1294 INT16 def_val, LPCSTR filename )
1296 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1297 * here, but Win98SE doesn't care about this at all, so I deleted it.
1298 * AFAIR versions prior to Win9x had these limits, though. */
1299 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1302 /***********************************************************************
1303 * GetPrivateProfileIntW (KERNEL32.@)
1305 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1306 INT def_val, LPCWSTR filename )
1308 WCHAR buffer[30];
1309 UNICODE_STRING bufferW;
1310 INT len;
1311 ULONG result;
1313 if (!(len = GetPrivateProfileStringW( section, entry, emptystringW,
1314 buffer, sizeof(buffer)/sizeof(WCHAR),
1315 filename )))
1316 return def_val;
1318 if (len+1 == sizeof(buffer)/sizeof(WCHAR)) FIXME("result may be wrong!");
1320 /* FIXME: if entry can be found but it's empty, then Win16 is
1321 * supposed to return 0 instead of def_val ! Difficult/problematic
1322 * to implement (every other failure also returns zero buffer),
1323 * thus wait until testing framework avail for making sure nothing
1324 * else gets broken that way. */
1325 if (!buffer[0]) return (UINT)def_val;
1327 RtlInitUnicodeString( &bufferW, buffer );
1328 RtlUnicodeStringToInteger( &bufferW, 10, &result);
1329 return result;
1332 /***********************************************************************
1333 * GetPrivateProfileIntA (KERNEL32.@)
1335 * FIXME: rewrite using unicode
1337 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1338 INT def_val, LPCSTR filename )
1340 UNICODE_STRING entryW, filenameW, sectionW;
1341 UINT res;
1342 if(entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1343 else entryW.Buffer = NULL;
1344 if(filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1345 else filenameW.Buffer = NULL;
1346 if(section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1347 else sectionW.Buffer = NULL;
1348 res = GetPrivateProfileIntW(sectionW.Buffer, entryW.Buffer, def_val,
1349 filenameW.Buffer);
1350 RtlFreeUnicodeString(&sectionW);
1351 RtlFreeUnicodeString(&filenameW);
1352 RtlFreeUnicodeString(&entryW);
1353 return res;
1356 /***********************************************************************
1357 * GetPrivateProfileSection (KERNEL.418)
1359 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1360 UINT16 len, LPCSTR filename )
1362 return GetPrivateProfileSectionA( section, buffer, len, filename );
1365 /***********************************************************************
1366 * GetPrivateProfileSectionW (KERNEL32.@)
1368 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1369 DWORD len, LPCWSTR filename )
1371 int ret = 0;
1373 EnterCriticalSection( &PROFILE_CritSect );
1375 if (PROFILE_Open( filename ))
1376 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1377 FALSE, TRUE);
1379 LeaveCriticalSection( &PROFILE_CritSect );
1381 return ret;
1384 /***********************************************************************
1385 * GetPrivateProfileSectionA (KERNEL32.@)
1387 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1388 DWORD len, LPCSTR filename )
1390 UNICODE_STRING sectionW, filenameW;
1391 LPWSTR bufferW;
1392 INT retW, ret = 0;
1394 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1395 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1396 else sectionW.Buffer = NULL;
1397 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1398 else filenameW.Buffer = NULL;
1400 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1401 if (len > 2)
1403 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1404 if (ret > 2)
1405 ret -= 2;
1406 else
1408 ret = 0;
1409 buffer[len-2] = 0;
1410 buffer[len-1] = 0;
1413 else
1415 buffer[0] = 0;
1416 buffer[1] = 0;
1419 RtlFreeUnicodeString(&sectionW);
1420 RtlFreeUnicodeString(&filenameW);
1421 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1422 return ret;
1425 /***********************************************************************
1426 * GetProfileSection (KERNEL.419)
1428 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1430 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1433 /***********************************************************************
1434 * GetProfileSectionA (KERNEL32.@)
1436 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1438 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1441 /***********************************************************************
1442 * GetProfileSectionW (KERNEL32.@)
1444 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1446 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1450 /***********************************************************************
1451 * WritePrivateProfileString (KERNEL.129)
1453 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1454 LPCSTR string, LPCSTR filename )
1456 return WritePrivateProfileStringA(section,entry,string,filename);
1459 /***********************************************************************
1460 * WritePrivateProfileStringW (KERNEL32.@)
1462 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1463 LPCWSTR string, LPCWSTR filename )
1465 BOOL ret = FALSE;
1467 EnterCriticalSection( &PROFILE_CritSect );
1469 if (PROFILE_Open( filename ))
1471 if (!section && !entry && !string) /* documented "file flush" case */
1473 PROFILE_FlushFile();
1474 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1476 else {
1477 if (!section) {
1478 FIXME("(NULL?,%s,%s,%s)?\n",
1479 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1480 } else {
1481 ret = PROFILE_SetString( section, entry, string, FALSE);
1482 PROFILE_FlushFile();
1487 LeaveCriticalSection( &PROFILE_CritSect );
1488 return ret;
1491 /***********************************************************************
1492 * WritePrivateProfileStringA (KERNEL32.@)
1494 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1495 LPCSTR string, LPCSTR filename )
1497 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1498 BOOL ret;
1500 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1501 else sectionW.Buffer = NULL;
1502 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1503 else entryW.Buffer = NULL;
1504 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1505 else stringW.Buffer = NULL;
1506 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1507 else filenameW.Buffer = NULL;
1509 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1510 stringW.Buffer, filenameW.Buffer);
1511 RtlFreeUnicodeString(&sectionW);
1512 RtlFreeUnicodeString(&entryW);
1513 RtlFreeUnicodeString(&stringW);
1514 RtlFreeUnicodeString(&filenameW);
1515 return ret;
1518 /***********************************************************************
1519 * WritePrivateProfileSection (KERNEL.416)
1521 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1522 LPCSTR string, LPCSTR filename )
1524 return WritePrivateProfileSectionA( section, string, filename );
1527 /***********************************************************************
1528 * WritePrivateProfileSectionW (KERNEL32.@)
1530 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1531 LPCWSTR string, LPCWSTR filename )
1533 BOOL ret = FALSE;
1534 LPWSTR p;
1536 EnterCriticalSection( &PROFILE_CritSect );
1538 if (PROFILE_Open( filename )) {
1539 if (!section && !string)
1540 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1541 else if (!string) {/* delete the named section*/
1542 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1543 PROFILE_FlushFile();
1544 } else {
1545 PROFILE_DeleteAllKeys(section);
1546 ret = TRUE;
1547 while(*string) {
1548 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1549 strcpyW( buf, string );
1550 if((p = strchrW( buf, '='))) {
1551 *p='\0';
1552 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1554 HeapFree( GetProcessHeap(), 0, buf );
1555 string += strlenW(string)+1;
1557 PROFILE_FlushFile();
1561 LeaveCriticalSection( &PROFILE_CritSect );
1562 return ret;
1565 /***********************************************************************
1566 * WritePrivateProfileSectionA (KERNEL32.@)
1568 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1569 LPCSTR string, LPCSTR filename)
1572 UNICODE_STRING sectionW, filenameW;
1573 LPWSTR stringW;
1574 BOOL ret;
1576 if (string)
1578 INT lenA, lenW;
1579 LPCSTR p = string;
1581 while(*p) p += strlen(p) + 1;
1582 lenA = p - string + 1;
1583 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1584 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1585 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1587 else stringW = NULL;
1588 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1589 else sectionW.Buffer = NULL;
1590 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1591 else filenameW.Buffer = NULL;
1593 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1595 HeapFree(GetProcessHeap(), 0, stringW);
1596 RtlFreeUnicodeString(&sectionW);
1597 RtlFreeUnicodeString(&filenameW);
1598 return ret;
1601 /***********************************************************************
1602 * WriteProfileSection (KERNEL.417)
1604 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1606 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1609 /***********************************************************************
1610 * WriteProfileSectionA (KERNEL32.@)
1612 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1615 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1618 /***********************************************************************
1619 * WriteProfileSectionW (KERNEL32.@)
1621 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1623 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1626 /***********************************************************************
1627 * GetPrivateProfileSectionNames (KERNEL.143)
1629 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1630 LPCSTR filename )
1632 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1636 /***********************************************************************
1637 * GetProfileSectionNames (KERNEL.142)
1639 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1642 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1646 /***********************************************************************
1647 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1649 * Returns the section names contained in the specified file.
1650 * FIXME: Where do we find this file when the path is relative?
1651 * The section names are returned as a list of strings with an extra
1652 * '\0' to mark the end of the list. Except for that the behavior
1653 * depends on the Windows version.
1655 * Win95:
1656 * - if the buffer is 0 or 1 character long then it is as if it was of
1657 * infinite length.
1658 * - otherwise, if the buffer is to small only the section names that fit
1659 * are returned.
1660 * - note that this means if the buffer was to small to return even just
1661 * the first section name then a single '\0' will be returned.
1662 * - the return value is the number of characters written in the buffer,
1663 * except if the buffer was too smal in which case len-2 is returned
1665 * Win2000:
1666 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1667 * '\0' and the return value is 0
1668 * - otherwise if the buffer is too small then the first section name that
1669 * does not fit is truncated so that the string list can be terminated
1670 * correctly (double '\0')
1671 * - the return value is the number of characters written in the buffer
1672 * except for the trailing '\0'. If the buffer is too small, then the
1673 * return value is len-2
1674 * - Win2000 has a bug that triggers when the section names and the
1675 * trailing '\0' fit exactly in the buffer. In that case the trailing
1676 * '\0' is missing.
1678 * Wine implements the observed Win2000 behavior (except for the bug).
1680 * Note that when the buffer is big enough then the return value may be any
1681 * value between 1 and len-1 (or len in Win95), including len-2.
1683 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1684 LPCWSTR filename)
1686 DWORD ret = 0;
1688 EnterCriticalSection( &PROFILE_CritSect );
1690 if (PROFILE_Open( filename ))
1691 ret = PROFILE_GetSectionNames(buffer, size);
1693 LeaveCriticalSection( &PROFILE_CritSect );
1695 return ret;
1699 /***********************************************************************
1700 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1702 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1703 LPCSTR filename)
1705 UNICODE_STRING filenameW;
1706 LPWSTR bufferW;
1707 INT retW, ret = 0;
1709 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1710 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1711 else filenameW.Buffer = NULL;
1713 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1714 if (retW && size)
1716 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1717 if (!ret)
1719 ret = size;
1720 buffer[size-1] = 0;
1724 RtlFreeUnicodeString(&filenameW);
1725 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1726 return ret;
1729 /***********************************************************************
1730 * GetPrivateProfileStruct (KERNEL.407)
1732 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1733 LPVOID buf, UINT16 len, LPCSTR filename)
1735 return GetPrivateProfileStructA( section, key, buf, len, filename );
1738 /***********************************************************************
1739 * GetPrivateProfileStructW (KERNEL32.@)
1741 * Should match Win95's behaviour pretty much
1743 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1744 LPVOID buf, UINT len, LPCWSTR filename)
1746 BOOL ret = FALSE;
1748 EnterCriticalSection( &PROFILE_CritSect );
1750 if (PROFILE_Open( filename )) {
1751 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1752 if (k) {
1753 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1754 if (((strlenW(k->value) - 2) / 2) == len)
1756 LPWSTR end, p;
1757 BOOL valid = TRUE;
1758 WCHAR c;
1759 DWORD chksum = 0;
1761 end = k->value + strlenW(k->value); /* -> '\0' */
1762 /* check for invalid chars in ASCII coded hex string */
1763 for (p=k->value; p < end; p++)
1765 if (!isxdigitW(*p))
1767 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1768 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1769 valid = FALSE;
1770 break;
1773 if (valid)
1775 BOOL highnibble = TRUE;
1776 BYTE b = 0, val;
1777 LPBYTE binbuf = (LPBYTE)buf;
1779 end -= 2; /* don't include checksum in output data */
1780 /* translate ASCII hex format into binary data */
1781 for (p=k->value; p < end; p++)
1783 c = toupperW(*p);
1784 val = (c > '9') ?
1785 (c - 'A' + 10) : (c - '0');
1787 if (highnibble)
1788 b = val << 4;
1789 else
1791 b += val;
1792 *binbuf++ = b; /* feed binary data into output */
1793 chksum += b; /* calculate checksum */
1795 highnibble ^= 1; /* toggle */
1797 /* retrieve stored checksum value */
1798 c = toupperW(*p++);
1799 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1800 c = toupperW(*p);
1801 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1802 if (b == (chksum & 0xff)) /* checksums match ? */
1803 ret = TRUE;
1808 LeaveCriticalSection( &PROFILE_CritSect );
1810 return ret;
1813 /***********************************************************************
1814 * GetPrivateProfileStructA (KERNEL32.@)
1816 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1817 LPVOID buffer, UINT len, LPCSTR filename)
1819 UNICODE_STRING sectionW, keyW, filenameW;
1820 INT ret;
1822 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1823 else sectionW.Buffer = NULL;
1824 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1825 else keyW.Buffer = NULL;
1826 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1827 else filenameW.Buffer = NULL;
1829 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1830 filenameW.Buffer);
1831 /* Do not translate binary data. */
1833 RtlFreeUnicodeString(&sectionW);
1834 RtlFreeUnicodeString(&keyW);
1835 RtlFreeUnicodeString(&filenameW);
1836 return ret;
1841 /***********************************************************************
1842 * WritePrivateProfileStruct (KERNEL.406)
1844 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1845 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1847 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1850 /***********************************************************************
1851 * WritePrivateProfileStructW (KERNEL32.@)
1853 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1854 LPVOID buf, UINT bufsize, LPCWSTR filename)
1856 BOOL ret = FALSE;
1857 LPBYTE binbuf;
1858 LPWSTR outstring, p;
1859 DWORD sum = 0;
1861 if (!section && !key && !buf) /* flush the cache */
1862 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1864 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1865 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1866 p = outstring;
1867 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1868 *p++ = hex[*binbuf >> 4];
1869 *p++ = hex[*binbuf & 0xf];
1870 sum += *binbuf;
1872 /* checksum is sum & 0xff */
1873 *p++ = hex[(sum & 0xf0) >> 4];
1874 *p++ = hex[sum & 0xf];
1875 *p++ = '\0';
1877 EnterCriticalSection( &PROFILE_CritSect );
1879 if (PROFILE_Open( filename )) {
1880 ret = PROFILE_SetString( section, key, outstring, FALSE);
1881 PROFILE_FlushFile();
1884 LeaveCriticalSection( &PROFILE_CritSect );
1886 HeapFree( GetProcessHeap(), 0, outstring );
1888 return ret;
1891 /***********************************************************************
1892 * WritePrivateProfileStructA (KERNEL32.@)
1894 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1895 LPVOID buf, UINT bufsize, LPCSTR filename)
1897 UNICODE_STRING sectionW, keyW, filenameW;
1898 INT ret;
1900 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1901 else sectionW.Buffer = NULL;
1902 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1903 else keyW.Buffer = NULL;
1904 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1905 else filenameW.Buffer = NULL;
1907 /* Do not translate binary data. */
1908 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1909 filenameW.Buffer);
1911 RtlFreeUnicodeString(&sectionW);
1912 RtlFreeUnicodeString(&keyW);
1913 RtlFreeUnicodeString(&filenameW);
1914 return ret;
1918 /***********************************************************************
1919 * WriteOutProfiles (KERNEL.315)
1921 void WINAPI WriteOutProfiles16(void)
1923 EnterCriticalSection( &PROFILE_CritSect );
1924 PROFILE_FlushFile();
1925 LeaveCriticalSection( &PROFILE_CritSect );
1928 /***********************************************************************
1929 * CloseProfileUserMapping (KERNEL32.@)
1931 BOOL WINAPI CloseProfileUserMapping(void) {
1932 FIXME("(), stub!\n");
1933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1934 return FALSE;