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
23 #include "wine/port.h"
32 #include <sys/types.h>
42 #include "wine/winbase16.h"
46 #include "wine/unicode.h"
47 #include "wine/server.h"
48 #include "wine/library.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(profile
);
53 typedef struct tagPROFILEKEY
56 struct tagPROFILEKEY
*next
;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY
*key
;
63 struct tagPROFILESECTION
*next
;
71 PROFILESECTION
*section
;
79 #define N_CACHED_PROFILES 10
81 /* Cached profile files */
82 static PROFILE
*MRUProfile
[N_CACHED_PROFILES
]={NULL
};
84 #define CurProfile (MRUProfile[0])
86 /* wine.ini config file registry root */
87 static HKEY wine_profile_key
;
89 #define PROFILE_MAX_LINE_LEN 1024
91 /* Wine profile: the profile file being used */
92 static char PROFILE_WineIniUsed
[MAX_PATHNAME_LEN
] = "";
94 /* Check for comments in profile */
95 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
97 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
99 static CRITICAL_SECTION PROFILE_CritSect
= CRITICAL_SECTION_INIT("PROFILE_CritSect");
101 static const char hex
[16] = "0123456789ABCDEF";
103 /***********************************************************************
106 * Copy the content of an entry into a buffer, removing quotes, and possibly
107 * translating environment variables.
109 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
110 int handle_env
, BOOL strip_quote
)
117 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
119 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
124 lstrcpynW( buffer
, value
, len
);
125 if (quote
&& (len
>= strlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
129 for (p
= value
; (*p
&& (len
> 1)); *buffer
++ = *p
++, len
-- )
131 if ((*p
== '$') && (p
[1] == '{'))
134 LPCWSTR p2
= strchrW( p
, '}' );
136 if (!p2
) continue; /* ignore it */
137 copy_len
= min( 1024, (int)(p2
-p
)-1 );
138 strncpyW(env_val
, p
+ 2, copy_len
);
139 env_val
[copy_len
- 1] = 0; /* ensure 0 termination */
141 if (GetEnvironmentVariableW( env_val
, buffer
, len
))
143 copy_len
= strlenW( buffer
);
150 if (quote
&& (len
> 1)) buffer
--;
155 /***********************************************************************
158 * Save a profile tree to a file.
160 static void PROFILE_Save( FILE *file
, PROFILESECTION
*section
)
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
);
178 WideCharToMultiByte(CP_ACP
, 0, key
->value
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
179 fprintf( file
, "=%s", buffer
);
181 fprintf( file
, "\r\n" );
187 /***********************************************************************
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) */
219 /***********************************************************************
222 * Load a profile tree from a file.
224 static PROFILESECTION
*PROFILE_Load( FILE *file
)
226 char buffer
[PROFILE_MAX_LINE_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
;
242 while (fgets( buffer
, PROFILE_MAX_LINE_LEN
, file
))
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",
259 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
261 MultiByteToWideChar(CP_ACP
, 0, p
, -1, section
->name
, len
+ 1);
263 section
->next
= NULL
;
264 *next_section
= section
;
265 next_section
= §ion
->next
;
266 next_key
= §ion
->key
;
269 TRACE("New section: %s\n", debugstr_w(section
->name
));
276 while ((p2
> p
) && ((*p2
== '\n') || PROFILE_isspace(*p2
))) *p2
--='\0';
278 if ((p2
= strchr( p
, '=' )) != NULL
)
281 while ((p3
> p
) && PROFILE_isspace(*p3
)) *p3
-- = '\0';
283 while (*p2
&& PROFILE_isspace(*p2
)) p2
++;
286 if(*p
|| !prev_key
|| *prev_key
->name
)
289 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
290 MultiByteToWideChar(CP_ACP
, 0, p
, -1, key
->name
, len
+ 1);
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
;
301 next_key
= &key
->next
;
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
)
321 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
323 PROFILESECTION
*to_del
= *section
;
324 *section
= to_del
->next
;
326 PROFILE_Free( to_del
);
329 section
= &(*section
)->next
;
335 /***********************************************************************
338 * Delete a key from a profile tree.
340 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
341 LPCWSTR section_name
, LPCWSTR key_name
)
345 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
347 PROFILEKEY
**key
= &(*section
)->key
;
350 if (!strcmpiW( (*key
)->name
, key_name
))
352 PROFILEKEY
*to_del
= *key
;
354 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
355 HeapFree( GetProcessHeap(), 0, to_del
);
361 section
= &(*section
)->next
;
367 /***********************************************************************
368 * PROFILE_DeleteAllKeys
370 * Delete all keys from a profile tree.
372 void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
374 PROFILESECTION
**section
= &CurProfile
->section
;
377 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
379 PROFILEKEY
**key
= &(*section
)->key
;
382 PROFILEKEY
*to_del
= *key
;
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 /***********************************************************************
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
)
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;
417 if ( ((*section
)->name
[0])
418 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
419 && (((*section
)->name
)[seclen
] == '\0') )
421 PROFILEKEY
**key
= &(*section
)->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.
431 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
432 && (((*key
)->name
)[keylen
] == '\0') )
437 if (!create
) return NULL
;
438 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
440 strcpyW( (*key
)->name
, key_name
);
441 (*key
)->value
= NULL
;
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
);
458 strcpyW( (*section
)->key
->name
, key_name
);
459 (*section
)->key
->value
= NULL
;
460 (*section
)->key
->next
= NULL
;
461 return (*section
)->key
;
465 /***********************************************************************
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
;
479 WARN("No current profile!\n");
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
);
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
);
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" );
510 WARN("could not save profile file %s\n", debugstr_w(CurProfile
->dos_name
));
514 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile
->dos_name
), unix_name
);
515 PROFILE_Save( file
, CurProfile
->section
);
517 CurProfile
->changed
= FALSE
;
518 if(!stat(unix_name
,&buf
))
519 CurProfile
->mtime
=buf
.st_mtime
;
524 /***********************************************************************
525 * PROFILE_ReleaseFile
527 * Flush the current profile to disk and remove it from the cache.
529 static void PROFILE_ReleaseFile(void)
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 /***********************************************************************
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
];
555 WCHAR
*name
, *name_lwr
;
560 PROFILE
*tempProfile
;
562 /* First time around */
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
;
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
)))
603 tempProfile
=MRUProfile
[i
];
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
);
612 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
613 debugstr_w(filename
), i
);
618 /* Flush the old current profile */
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
);
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
);
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
);
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
);
672 CurProfile
->section
= PROFILE_Load( file
);
674 if(!stat(CurProfile
->unix_name
,&buf
))
675 CurProfile
->mtime
=buf
.st_mtime
;
679 /* Does not exist yet, we will create it in PROFILE_FlushFile */
680 WARN("profile file %s not found\n", debugstr_w(newdos_name
) );
686 /***********************************************************************
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
,
698 if(!buffer
) return 0;
700 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
704 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
707 for (key
= section
->key
; key
; key
= key
->next
)
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;
717 if (return_values
&& key
->value
) {
719 PROFILE_CopyEntry ( buffer
,
720 key
->value
, len
- 1, handle_env
, 0 );
721 len
-= strlenW(buffer
) + 1;
722 buffer
+= strlenW(buffer
) + 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
738 section
= section
->next
;
740 buffer
[0] = buffer
[1] = '\0';
744 /* See GetPrivateProfileSectionNamesA for documentation */
745 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
749 PROFILESECTION
*section
;
760 section
= CurProfile
->section
;
761 while ((section
!=NULL
)) {
762 if (section
->name
[0]) {
763 l
= strlenW(section
->name
)+1;
766 strncpyW(buf
, section
->name
, f
-1);
773 strcpyW(buf
, section
->name
);
777 section
= section
->next
;
784 /***********************************************************************
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 "" (!)
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
;
819 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
822 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
823 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
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
);
846 /***********************************************************************
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
,
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
;
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
;
899 /***********************************************************************
900 * PROFILE_GetWineIniString
902 * Get a config string from the wine.ini file.
904 int PROFILE_GetWineIniString( LPCWSTR section
, LPCWSTR key_name
,
905 LPCWSTR def
, LPWSTR buffer
, int len
)
909 OBJECT_ATTRIBUTES attr
;
910 UNICODE_STRING nameW
;
912 attr
.Length
= sizeof(attr
);
913 attr
.RootDirectory
= wine_profile_key
;
914 attr
.ObjectName
= &nameW
;
916 attr
.SecurityDescriptor
= NULL
;
917 attr
.SecurityQualityOfService
= NULL
;
918 RtlInitUnicodeString( &nameW
, section
);
919 if (!(err
= NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)))
921 char tmp
[PROFILE_MAX_LINE_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
924 RtlInitUnicodeString( &nameW
, key_name
);
925 if (!(err
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
926 tmp
, sizeof(tmp
), &count
)))
928 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
929 PROFILE_CopyEntry( buffer
, str
, len
, TRUE
, TRUE
);
934 if (err
) PROFILE_CopyEntry( buffer
, def
, len
, TRUE
, TRUE
);
935 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section
),
936 debugstr_w(key_name
), debugstr_w(def
), debugstr_w(buffer
) );
937 return strlenW(buffer
);
941 /******************************************************************************
943 * PROFILE_GetWineIniBool
945 * Reads a boolean value from the wine.ini file. This function attempts to
946 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
947 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
948 * true. Anything else results in the return of the default value.
950 * This function uses 1 to indicate true, and 0 for false. You can check
951 * for existence by setting def to something other than 0 or 1 and
952 * examining the return value.
954 int PROFILE_GetWineIniBool( LPCWSTR section
, LPCWSTR key_name
, int def
)
956 static const WCHAR def_valueW
[] = {'~',0};
960 PROFILE_GetWineIniString(section
, key_name
, def_valueW
, key_value
, 2);
962 switch(key_value
[0]) {
983 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section
), debugstr_w(key_name
),
984 def
? "TRUE" : "FALSE", key_value
[0],
985 retval
? "TRUE" : "FALSE");
991 /***********************************************************************
992 * PROFILE_LoadWineIni
994 * Load the old .winerc file.
996 int PROFILE_LoadWineIni(void)
998 OBJECT_ATTRIBUTES attr
;
999 UNICODE_STRING nameW
;
1000 char buffer
[MAX_PATHNAME_LEN
];
1006 attr
.Length
= sizeof(attr
);
1007 attr
.RootDirectory
= 0;
1008 attr
.ObjectName
= &nameW
;
1009 attr
.Attributes
= 0;
1010 attr
.SecurityDescriptor
= NULL
;
1011 attr
.SecurityQualityOfService
= NULL
;
1013 /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1014 if (!RtlCreateUnicodeStringFromAsciiz( &nameW
, "Machine\\Software\\Wine\\Wine" ) ||
1015 NtCreateKey( &hKeySW
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, 0, &disp
))
1017 ERR("Cannot create config registry key\n" );
1020 RtlFreeUnicodeString( &nameW
);
1023 if (!RtlCreateUnicodeStringFromAsciiz( &nameW
, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1024 NtCreateKey( &wine_profile_key
, KEY_ALL_ACCESS
, &attr
, 0,
1025 NULL
, REG_OPTION_VOLATILE
, &disp
))
1027 ERR("Cannot create config registry key\n" );
1030 RtlFreeUnicodeString( &nameW
);
1032 if (disp
== REG_OPENED_EXISTING_KEY
) return 1; /* loaded by the server */
1034 MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
1039 /***********************************************************************
1040 * PROFILE_UsageWineIni
1042 * Explain the wine.ini file to those who don't read documentation.
1043 * Keep below one screenful in length so that error messages above are
1046 void PROFILE_UsageWineIni(void)
1048 MESSAGE("Perhaps you have not properly edited or created "
1049 "your Wine configuration file.\n");
1050 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1051 /* RTFM, so to say */
1055 /********************* API functions **********************************/
1057 /***********************************************************************
1058 * GetProfileInt (KERNEL.57)
1060 UINT16 WINAPI
GetProfileInt16( LPCSTR section
, LPCSTR entry
, INT16 def_val
)
1062 return GetPrivateProfileInt16( section
, entry
, def_val
, "win.ini" );
1066 /***********************************************************************
1067 * GetProfileIntA (KERNEL32.@)
1069 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1071 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1074 /***********************************************************************
1075 * GetProfileIntW (KERNEL32.@)
1077 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1079 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1083 * if allow_section_name_copy is TRUE, allow the copying :
1084 * - of Section names if 'section' is NULL
1085 * - of Keys in a Section if 'entry' is NULL
1086 * (see MSDN doc for GetPrivateProfileString)
1088 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1089 LPCWSTR def_val
, LPWSTR buffer
,
1090 UINT len
, LPCWSTR filename
,
1091 BOOL allow_section_name_copy
)
1094 LPWSTR pDefVal
= NULL
;
1099 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1100 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1102 /* strip any trailing ' ' of def_val. */
1105 LPCWSTR p
= &def_val
[strlenW(def_val
)]; /* even "" works ! */
1113 if (*p
== ' ') /* ouch, contained trailing ' ' */
1115 int len
= (int)(p
- def_val
);
1116 pDefVal
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1117 strncpyW(pDefVal
, def_val
, len
);
1118 pDefVal
[len
] = '\0';
1122 pDefVal
= (LPWSTR
)def_val
;
1124 EnterCriticalSection( &PROFILE_CritSect
);
1126 if (PROFILE_Open( filename
)) {
1127 if ((allow_section_name_copy
) && (section
== NULL
))
1128 ret
= PROFILE_GetSectionNames(buffer
, len
);
1130 /* PROFILE_GetString already handles the 'entry == NULL' case */
1131 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1133 lstrcpynW( buffer
, pDefVal
, len
);
1134 ret
= strlenW( buffer
);
1137 LeaveCriticalSection( &PROFILE_CritSect
);
1139 if (pDefVal
!= def_val
) /* allocated */
1140 HeapFree(GetProcessHeap(), 0, pDefVal
);
1142 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1147 /***********************************************************************
1148 * GetPrivateProfileString (KERNEL.128)
1150 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1151 LPCSTR def_val
, LPSTR buffer
,
1152 UINT16 len
, LPCSTR filename
)
1154 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1156 INT16 retW
, ret
= 0;
1158 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1159 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1160 else sectionW
.Buffer
= NULL
;
1161 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1162 else entryW
.Buffer
= NULL
;
1163 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1164 else def_valW
.Buffer
= NULL
;
1165 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1166 else filenameW
.Buffer
= NULL
;
1168 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1169 def_valW
.Buffer
, bufferW
, len
,
1170 filenameW
.Buffer
, FALSE
);
1173 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1180 ret
--; /* strip terminating 0 */
1183 RtlFreeUnicodeString(§ionW
);
1184 RtlFreeUnicodeString(&entryW
);
1185 RtlFreeUnicodeString(&def_valW
);
1186 RtlFreeUnicodeString(&filenameW
);
1187 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1191 /***********************************************************************
1192 * GetPrivateProfileStringA (KERNEL32.@)
1194 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1195 LPCSTR def_val
, LPSTR buffer
,
1196 UINT len
, LPCSTR filename
)
1198 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1202 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1203 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1204 else sectionW
.Buffer
= NULL
;
1205 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1206 else entryW
.Buffer
= NULL
;
1207 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1208 else def_valW
.Buffer
= NULL
;
1209 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1210 else filenameW
.Buffer
= NULL
;
1212 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1213 def_valW
.Buffer
, bufferW
, len
,
1217 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1224 ret
--; /* strip terminating 0 */
1227 RtlFreeUnicodeString(§ionW
);
1228 RtlFreeUnicodeString(&entryW
);
1229 RtlFreeUnicodeString(&def_valW
);
1230 RtlFreeUnicodeString(&filenameW
);
1231 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1235 /***********************************************************************
1236 * GetPrivateProfileStringW (KERNEL32.@)
1238 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1239 LPCWSTR def_val
, LPWSTR buffer
,
1240 UINT len
, LPCWSTR filename
)
1242 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1243 buffer
, len
, filename
, TRUE
);
1246 /***********************************************************************
1247 * GetProfileString (KERNEL.58)
1249 INT16 WINAPI
GetProfileString16( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1250 LPSTR buffer
, UINT16 len
)
1252 return GetPrivateProfileString16( section
, entry
, def_val
,
1253 buffer
, len
, "win.ini" );
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 * WriteProfileString (KERNEL.59)
1279 BOOL16 WINAPI
WriteProfileString16( LPCSTR section
, LPCSTR entry
,
1282 return WritePrivateProfileString16( section
, entry
, string
, "win.ini" );
1285 /***********************************************************************
1286 * WriteProfileStringA (KERNEL32.@)
1288 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1291 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1294 /***********************************************************************
1295 * WriteProfileStringW (KERNEL32.@)
1297 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1300 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1304 /***********************************************************************
1305 * GetPrivateProfileInt (KERNEL.127)
1307 UINT16 WINAPI
GetPrivateProfileInt16( LPCSTR section
, LPCSTR entry
,
1308 INT16 def_val
, LPCSTR filename
)
1310 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1311 * here, but Win98SE doesn't care about this at all, so I deleted it.
1312 * AFAIR versions prior to Win9x had these limits, though. */
1313 return (INT16
)GetPrivateProfileIntA(section
,entry
,def_val
,filename
);
1316 /***********************************************************************
1317 * GetPrivateProfileIntA (KERNEL32.@)
1319 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1320 INT def_val
, LPCSTR filename
)
1325 if (!GetPrivateProfileStringA( section
, entry
, "",
1326 buffer
, sizeof(buffer
), filename
))
1328 /* FIXME: if entry can be found but it's empty, then Win16 is
1329 * supposed to return 0 instead of def_val ! Difficult/problematic
1330 * to implement (every other failure also returns zero buffer),
1331 * thus wait until testing framework avail for making sure nothing
1332 * else gets broken that way. */
1333 if (!buffer
[0]) return (UINT
)def_val
;
1335 /* Don't use strtol() here !
1336 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1337 YES, scan for unsigned format ! (otherwise compatibility error) */
1338 if (!sscanf(buffer
, "%lu", &result
)) return 0;
1339 return (UINT
)result
;
1342 /***********************************************************************
1343 * GetPrivateProfileIntW (KERNEL32.@)
1345 * FIXME: rewrite using unicode
1347 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1348 INT def_val
, LPCWSTR filename
)
1350 LPSTR sectionA
= HEAP_strdupWtoA( GetProcessHeap(), 0, section
);
1351 LPSTR entryA
= HEAP_strdupWtoA( GetProcessHeap(), 0, entry
);
1352 LPSTR filenameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
1353 UINT res
= GetPrivateProfileIntA(sectionA
, entryA
, def_val
, filenameA
);
1354 HeapFree( GetProcessHeap(), 0, sectionA
);
1355 HeapFree( GetProcessHeap(), 0, filenameA
);
1356 HeapFree( GetProcessHeap(), 0, entryA
);
1360 /***********************************************************************
1361 * GetPrivateProfileSection (KERNEL.418)
1363 INT16 WINAPI
GetPrivateProfileSection16( LPCSTR section
, LPSTR buffer
,
1364 UINT16 len
, LPCSTR filename
)
1366 return GetPrivateProfileSectionA( section
, buffer
, len
, filename
);
1369 /***********************************************************************
1370 * GetPrivateProfileSectionW (KERNEL32.@)
1372 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1373 DWORD len
, LPCWSTR filename
)
1377 EnterCriticalSection( &PROFILE_CritSect
);
1379 if (PROFILE_Open( filename
))
1380 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
,
1383 LeaveCriticalSection( &PROFILE_CritSect
);
1388 /***********************************************************************
1389 * GetPrivateProfileSectionA (KERNEL32.@)
1391 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1392 DWORD len
, LPCSTR filename
)
1394 UNICODE_STRING sectionW
, filenameW
;
1398 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1399 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1400 else sectionW
.Buffer
= NULL
;
1401 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1402 else filenameW
.Buffer
= NULL
;
1404 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1407 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1423 RtlFreeUnicodeString(§ionW
);
1424 RtlFreeUnicodeString(&filenameW
);
1425 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1429 /***********************************************************************
1430 * GetProfileSection (KERNEL.419)
1432 INT16 WINAPI
GetProfileSection16( LPCSTR section
, LPSTR buffer
, UINT16 len
)
1434 return GetPrivateProfileSection16( section
, buffer
, len
, "win.ini" );
1437 /***********************************************************************
1438 * GetProfileSectionA (KERNEL32.@)
1440 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1442 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1445 /***********************************************************************
1446 * GetProfileSectionW (KERNEL32.@)
1448 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1450 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1454 /***********************************************************************
1455 * WritePrivateProfileString (KERNEL.129)
1457 BOOL16 WINAPI
WritePrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1458 LPCSTR string
, LPCSTR filename
)
1460 return WritePrivateProfileStringA(section
,entry
,string
,filename
);
1463 /***********************************************************************
1464 * WritePrivateProfileStringW (KERNEL32.@)
1466 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1467 LPCWSTR string
, LPCWSTR filename
)
1471 EnterCriticalSection( &PROFILE_CritSect
);
1473 if (PROFILE_Open( filename
))
1475 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1477 PROFILE_FlushFile();
1478 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1482 FIXME("(NULL?,%s,%s,%s)?\n",
1483 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1485 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1486 PROFILE_FlushFile();
1491 LeaveCriticalSection( &PROFILE_CritSect
);
1495 /***********************************************************************
1496 * WritePrivateProfileStringA (KERNEL32.@)
1498 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1499 LPCSTR string
, LPCSTR filename
)
1501 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1504 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1505 else sectionW
.Buffer
= NULL
;
1506 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1507 else entryW
.Buffer
= NULL
;
1508 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1509 else stringW
.Buffer
= NULL
;
1510 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1511 else filenameW
.Buffer
= NULL
;
1513 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1514 stringW
.Buffer
, filenameW
.Buffer
);
1515 RtlFreeUnicodeString(§ionW
);
1516 RtlFreeUnicodeString(&entryW
);
1517 RtlFreeUnicodeString(&stringW
);
1518 RtlFreeUnicodeString(&filenameW
);
1522 /***********************************************************************
1523 * WritePrivateProfileSection (KERNEL.416)
1525 BOOL16 WINAPI
WritePrivateProfileSection16( LPCSTR section
,
1526 LPCSTR string
, LPCSTR filename
)
1528 return WritePrivateProfileSectionA( section
, string
, filename
);
1531 /***********************************************************************
1532 * WritePrivateProfileSectionW (KERNEL32.@)
1534 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1535 LPCWSTR string
, LPCWSTR filename
)
1540 EnterCriticalSection( &PROFILE_CritSect
);
1542 if (PROFILE_Open( filename
)) {
1543 if (!section
&& !string
)
1544 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1545 else if (!string
) {/* delete the named section*/
1546 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1547 PROFILE_FlushFile();
1549 PROFILE_DeleteAllKeys(section
);
1552 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1553 strcpyW( buf
, string
);
1554 if((p
= strchrW( buf
, '='))) {
1556 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1558 HeapFree( GetProcessHeap(), 0, buf
);
1559 string
+= strlenW(string
)+1;
1561 PROFILE_FlushFile();
1565 LeaveCriticalSection( &PROFILE_CritSect
);
1569 /***********************************************************************
1570 * WritePrivateProfileSectionA (KERNEL32.@)
1572 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1573 LPCSTR string
, LPCSTR filename
)
1576 UNICODE_STRING sectionW
, filenameW
;
1585 while(*p
) p
+= strlen(p
) + 1;
1586 lenA
= p
- string
+ 1;
1587 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1588 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1589 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1591 else stringW
= NULL
;
1592 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1593 else sectionW
.Buffer
= NULL
;
1594 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1595 else filenameW
.Buffer
= NULL
;
1597 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1599 HeapFree(GetProcessHeap(), 0, stringW
);
1600 RtlFreeUnicodeString(§ionW
);
1601 RtlFreeUnicodeString(&filenameW
);
1605 /***********************************************************************
1606 * WriteProfileSection (KERNEL.417)
1608 BOOL16 WINAPI
WriteProfileSection16( LPCSTR section
, LPCSTR keys_n_values
)
1610 return WritePrivateProfileSection16( section
, keys_n_values
, "win.ini");
1613 /***********************************************************************
1614 * WriteProfileSectionA (KERNEL32.@)
1616 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1619 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1622 /***********************************************************************
1623 * WriteProfileSectionW (KERNEL32.@)
1625 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1627 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1630 /***********************************************************************
1631 * GetPrivateProfileSectionNames (KERNEL.143)
1633 WORD WINAPI
GetPrivateProfileSectionNames16( LPSTR buffer
, WORD size
,
1636 return GetPrivateProfileSectionNamesA(buffer
,size
,filename
);
1640 /***********************************************************************
1641 * GetProfileSectionNames (KERNEL.142)
1643 WORD WINAPI
GetProfileSectionNames16(LPSTR buffer
, WORD size
)
1646 return GetPrivateProfileSectionNamesA(buffer
,size
,"win.ini");
1650 /***********************************************************************
1651 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1653 * Returns the section names contained in the specified file.
1654 * FIXME: Where do we find this file when the path is relative?
1655 * The section names are returned as a list of strings with an extra
1656 * '\0' to mark the end of the list. Except for that the behavior
1657 * depends on the Windows version.
1660 * - if the buffer is 0 or 1 character long then it is as if it was of
1662 * - otherwise, if the buffer is to small only the section names that fit
1664 * - note that this means if the buffer was to small to return even just
1665 * the first section name then a single '\0' will be returned.
1666 * - the return value is the number of characters written in the buffer,
1667 * except if the buffer was too smal in which case len-2 is returned
1670 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1671 * '\0' and the return value is 0
1672 * - otherwise if the buffer is too small then the first section name that
1673 * does not fit is truncated so that the string list can be terminated
1674 * correctly (double '\0')
1675 * - the return value is the number of characters written in the buffer
1676 * except for the trailing '\0'. If the buffer is too small, then the
1677 * return value is len-2
1678 * - Win2000 has a bug that triggers when the section names and the
1679 * trailing '\0' fit exactly in the buffer. In that case the trailing
1682 * Wine implements the observed Win2000 behavior (except for the bug).
1684 * Note that when the buffer is big enough then the return value may be any
1685 * value between 1 and len-1 (or len in Win95), including len-2.
1687 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1692 EnterCriticalSection( &PROFILE_CritSect
);
1694 if (PROFILE_Open( filename
))
1695 ret
= PROFILE_GetSectionNames(buffer
, size
);
1697 LeaveCriticalSection( &PROFILE_CritSect
);
1703 /***********************************************************************
1704 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1706 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1709 UNICODE_STRING filenameW
;
1713 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1714 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1715 else filenameW
.Buffer
= NULL
;
1717 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1720 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1728 RtlFreeUnicodeString(&filenameW
);
1729 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1733 /***********************************************************************
1734 * GetPrivateProfileStruct (KERNEL.407)
1736 BOOL16 WINAPI
GetPrivateProfileStruct16(LPCSTR section
, LPCSTR key
,
1737 LPVOID buf
, UINT16 len
, LPCSTR filename
)
1739 return GetPrivateProfileStructA( section
, key
, buf
, len
, filename
);
1742 /***********************************************************************
1743 * GetPrivateProfileStructW (KERNEL32.@)
1745 * Should match Win95's behaviour pretty much
1747 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1748 LPVOID buf
, UINT len
, LPCWSTR filename
)
1752 EnterCriticalSection( &PROFILE_CritSect
);
1754 if (PROFILE_Open( filename
)) {
1755 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1757 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1758 if (((strlenW(k
->value
) - 2) / 2) == len
)
1765 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1766 /* check for invalid chars in ASCII coded hex string */
1767 for (p
=k
->value
; p
< end
; p
++)
1771 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1772 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1779 BOOL highnibble
= TRUE
;
1781 LPBYTE binbuf
= (LPBYTE
)buf
;
1783 end
-= 2; /* don't include checksum in output data */
1784 /* translate ASCII hex format into binary data */
1785 for (p
=k
->value
; p
< end
; p
++)
1789 (c
- 'A' + 10) : (c
- '0');
1796 *binbuf
++ = b
; /* feed binary data into output */
1797 chksum
+= b
; /* calculate checksum */
1799 highnibble
^= 1; /* toggle */
1801 /* retrieve stored checksum value */
1803 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1805 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1806 if (b
== (chksum
& 0xff)) /* checksums match ? */
1812 LeaveCriticalSection( &PROFILE_CritSect
);
1817 /***********************************************************************
1818 * GetPrivateProfileStructA (KERNEL32.@)
1820 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1821 LPVOID buffer
, UINT len
, LPCSTR filename
)
1823 UNICODE_STRING sectionW
, keyW
, filenameW
;
1826 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1827 else sectionW
.Buffer
= NULL
;
1828 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1829 else keyW
.Buffer
= NULL
;
1830 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1831 else filenameW
.Buffer
= NULL
;
1833 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1835 /* Do not translate binary data. */
1837 RtlFreeUnicodeString(§ionW
);
1838 RtlFreeUnicodeString(&keyW
);
1839 RtlFreeUnicodeString(&filenameW
);
1845 /***********************************************************************
1846 * WritePrivateProfileStruct (KERNEL.406)
1848 BOOL16 WINAPI
WritePrivateProfileStruct16 (LPCSTR section
, LPCSTR key
,
1849 LPVOID buf
, UINT16 bufsize
, LPCSTR filename
)
1851 return WritePrivateProfileStructA( section
, key
, buf
, bufsize
, filename
);
1854 /***********************************************************************
1855 * WritePrivateProfileStructW (KERNEL32.@)
1857 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1858 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1862 LPWSTR outstring
, p
;
1865 if (!section
&& !key
&& !buf
) /* flush the cache */
1866 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1868 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1869 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1871 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1872 *p
++ = hex
[*binbuf
>> 4];
1873 *p
++ = hex
[*binbuf
& 0xf];
1876 /* checksum is sum & 0xff */
1877 *p
++ = hex
[(sum
& 0xf0) >> 4];
1878 *p
++ = hex
[sum
& 0xf];
1881 EnterCriticalSection( &PROFILE_CritSect
);
1883 if (PROFILE_Open( filename
)) {
1884 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1885 PROFILE_FlushFile();
1888 LeaveCriticalSection( &PROFILE_CritSect
);
1890 HeapFree( GetProcessHeap(), 0, outstring
);
1895 /***********************************************************************
1896 * WritePrivateProfileStructA (KERNEL32.@)
1898 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1899 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1901 UNICODE_STRING sectionW
, keyW
, filenameW
;
1904 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1905 else sectionW
.Buffer
= NULL
;
1906 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1907 else keyW
.Buffer
= NULL
;
1908 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1909 else filenameW
.Buffer
= NULL
;
1911 /* Do not translate binary data. */
1912 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1915 RtlFreeUnicodeString(§ionW
);
1916 RtlFreeUnicodeString(&keyW
);
1917 RtlFreeUnicodeString(&filenameW
);
1922 /***********************************************************************
1923 * WriteOutProfiles (KERNEL.315)
1925 void WINAPI
WriteOutProfiles16(void)
1927 EnterCriticalSection( &PROFILE_CritSect
);
1928 PROFILE_FlushFile();
1929 LeaveCriticalSection( &PROFILE_CritSect
);
1932 /***********************************************************************
1933 * CloseProfileUserMapping (KERNEL32.@)
1935 BOOL WINAPI
CloseProfileUserMapping(void) {
1936 FIXME("(), stub!\n");
1937 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);