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"
31 #include <sys/types.h>
41 #include "wine/winbase16.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
55 struct tagPROFILEKEY
*next
;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY
*key
;
62 struct tagPROFILESECTION
*next
;
70 PROFILESECTION
*section
;
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
;
94 static CRITICAL_SECTION_DEBUG critsect_debug
=
96 0, 0, &PROFILE_CritSect
,
97 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
98 0, 0, { 0, (DWORD
)(__FILE__
": PROFILE_CritSect") }
100 static CRITICAL_SECTION PROFILE_CritSect
= { &critsect_debug
, -1, 0, 0, 0, 0 };
102 static const char hex
[16] = "0123456789ABCDEF";
104 /***********************************************************************
107 * Copy the content of an entry into a buffer, removing quotes, and possibly
108 * translating environment variables.
110 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
111 int handle_env
, BOOL strip_quote
)
118 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
120 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
125 lstrcpynW( buffer
, value
, len
);
126 if (quote
&& (len
>= strlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
131 while (*p
&& (len
> 1))
133 if ((*p
== '$') && (p
[1] == '{'))
136 LPCWSTR p2
= strchrW( p
, '}' );
138 if (!p2
) continue; /* ignore it */
139 copy_len
= min( 1024, (int)(p2
-p
)-1 );
140 strncpyW(env_val
, p
+ 2, copy_len
);
141 env_val
[copy_len
- 1] = 0; /* ensure 0 termination */
143 if (GetEnvironmentVariableW( env_val
, buffer
, len
))
145 copy_len
= strlenW( buffer
);
157 if (quote
&& (len
> 1)) buffer
--;
162 /***********************************************************************
165 * Save a profile tree to a file.
167 static void PROFILE_Save( FILE *file
, PROFILESECTION
*section
)
170 char buffer
[PROFILE_MAX_LINE_LEN
];
172 for ( ; section
; section
= section
->next
)
174 if (section
->name
[0])
176 WideCharToMultiByte(CP_ACP
, 0, section
->name
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
177 fprintf( file
, "\r\n[%s]\r\n", buffer
);
179 for (key
= section
->key
; key
; key
= key
->next
)
181 WideCharToMultiByte(CP_ACP
, 0, key
->name
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
182 fprintf( file
, "%s", buffer
);
185 WideCharToMultiByte(CP_ACP
, 0, key
->value
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
186 fprintf( file
, "=%s", buffer
);
188 fprintf( file
, "\r\n" );
194 /***********************************************************************
197 * Free a profile tree.
199 static void PROFILE_Free( PROFILESECTION
*section
)
201 PROFILESECTION
*next_section
;
202 PROFILEKEY
*key
, *next_key
;
204 for ( ; section
; section
= next_section
)
206 for (key
= section
->key
; key
; key
= next_key
)
208 next_key
= key
->next
;
209 if (key
->value
) HeapFree( GetProcessHeap(), 0, key
->value
);
210 HeapFree( GetProcessHeap(), 0, key
);
212 next_section
= section
->next
;
213 HeapFree( GetProcessHeap(), 0, section
);
217 static inline int PROFILE_isspace(char c
)
219 if (isspace(c
)) return 1;
220 if (c
=='\r' || c
==0x1a) return 1;
221 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
226 /***********************************************************************
229 * Load a profile tree from a file.
231 static PROFILESECTION
*PROFILE_Load( FILE *file
)
233 char buffer
[PROFILE_MAX_LINE_LEN
];
236 PROFILESECTION
*section
, *first_section
;
237 PROFILESECTION
**next_section
;
238 PROFILEKEY
*key
, *prev_key
, **next_key
;
240 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
241 if(first_section
== NULL
) return NULL
;
242 first_section
->name
[0] = 0;
243 first_section
->key
= NULL
;
244 first_section
->next
= NULL
;
245 next_section
= &first_section
->next
;
246 next_key
= &first_section
->key
;
249 while (fgets( buffer
, PROFILE_MAX_LINE_LEN
, file
))
253 while (*p
&& PROFILE_isspace(*p
)) p
++;
254 if (*p
== '[') /* section start */
256 if (!(p2
= strrchr( p
, ']' )))
258 WARN("Invalid section header at line %d: '%s'\n",
266 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
268 MultiByteToWideChar(CP_ACP
, 0, p
, -1, section
->name
, len
+ 1);
270 section
->next
= NULL
;
271 *next_section
= section
;
272 next_section
= §ion
->next
;
273 next_key
= §ion
->key
;
276 TRACE("New section: %s\n", debugstr_w(section
->name
));
283 while ((p2
> p
) && ((*p2
== '\n') || PROFILE_isspace(*p2
))) *p2
--='\0';
285 if ((p2
= strchr( p
, '=' )) != NULL
)
288 while ((p3
> p
) && PROFILE_isspace(*p3
)) *p3
-- = '\0';
290 while (*p2
&& PROFILE_isspace(*p2
)) p2
++;
293 if(*p
|| !prev_key
|| *prev_key
->name
)
296 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
297 MultiByteToWideChar(CP_ACP
, 0, p
, -1, key
->name
, len
+ 1);
300 len
= strlen(p2
) + 1;
301 key
->value
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
302 MultiByteToWideChar(CP_ACP
, 0, p2
, -1, key
->value
, len
);
304 else key
->value
= NULL
;
308 next_key
= &key
->next
;
311 TRACE("New key: name=%s, value=%s\n",
312 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
315 return first_section
;
319 /***********************************************************************
320 * PROFILE_DeleteSection
322 * Delete a section from a profile tree.
324 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
328 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
330 PROFILESECTION
*to_del
= *section
;
331 *section
= to_del
->next
;
333 PROFILE_Free( to_del
);
336 section
= &(*section
)->next
;
342 /***********************************************************************
345 * Delete a key from a profile tree.
347 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
348 LPCWSTR section_name
, LPCWSTR key_name
)
352 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
354 PROFILEKEY
**key
= &(*section
)->key
;
357 if (!strcmpiW( (*key
)->name
, key_name
))
359 PROFILEKEY
*to_del
= *key
;
361 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
362 HeapFree( GetProcessHeap(), 0, to_del
);
368 section
= &(*section
)->next
;
374 /***********************************************************************
375 * PROFILE_DeleteAllKeys
377 * Delete all keys from a profile tree.
379 void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
381 PROFILESECTION
**section
= &CurProfile
->section
;
384 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
386 PROFILEKEY
**key
= &(*section
)->key
;
389 PROFILEKEY
*to_del
= *key
;
391 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
392 HeapFree( GetProcessHeap(), 0, to_del
);
393 CurProfile
->changed
=TRUE
;
396 section
= &(*section
)->next
;
401 /***********************************************************************
404 * Find a key in a profile tree, optionally creating it.
406 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
407 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
412 while (PROFILE_isspace(*section_name
)) section_name
++;
413 p
= section_name
+ strlenW(section_name
) - 1;
414 while ((p
> section_name
) && PROFILE_isspace(*p
)) p
--;
415 seclen
= p
- section_name
+ 1;
417 while (PROFILE_isspace(*key_name
)) key_name
++;
418 p
= key_name
+ strlenW(key_name
) - 1;
419 while ((p
> key_name
) && PROFILE_isspace(*p
)) p
--;
420 keylen
= p
- key_name
+ 1;
424 if ( ((*section
)->name
[0])
425 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
426 && (((*section
)->name
)[seclen
] == '\0') )
428 PROFILEKEY
**key
= &(*section
)->key
;
432 /* If create_always is FALSE then we check if the keyname
433 * already exists. Otherwise we add it regardless of its
434 * existence, to allow keys to be added more than once in
439 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
440 && (((*key
)->name
)[keylen
] == '\0') )
445 if (!create
) return NULL
;
446 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
448 strcpyW( (*key
)->name
, key_name
);
449 (*key
)->value
= NULL
;
453 section
= &(*section
)->next
;
455 if (!create
) return NULL
;
456 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
457 if(*section
== NULL
) return NULL
;
458 strcpyW( (*section
)->name
, section_name
);
459 (*section
)->next
= NULL
;
460 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
461 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
463 HeapFree(GetProcessHeap(), 0, *section
);
466 strcpyW( (*section
)->key
->name
, key_name
);
467 (*section
)->key
->value
= NULL
;
468 (*section
)->key
->next
= NULL
;
469 return (*section
)->key
;
473 /***********************************************************************
476 * Flush the current profile to disk if changed.
478 static BOOL
PROFILE_FlushFile(void)
480 char *p
, buffer
[MAX_PATHNAME_LEN
];
481 const char *unix_name
;
487 WARN("No current profile!\n");
491 if (!CurProfile
->changed
|| !CurProfile
->dos_name
) return TRUE
;
492 if (!(unix_name
= CurProfile
->unix_name
) || !(file
= fopen(unix_name
, "w")))
494 int drive
= toupperW(CurProfile
->dos_name
[0]) - 'A';
495 WCHAR
*name
, *name_lwr
;
496 /* Try to create it in $HOME/.wine */
497 /* FIXME: this will need a more general solution */
498 strcpy( buffer
, wine_get_config_dir() );
499 p
= buffer
+ strlen(buffer
);
501 *p
= 0; /* make strlen() below happy */
502 name
= strrchrW( CurProfile
->dos_name
, '\\' ) + 1;
504 /* create a lower cased version of the name */
505 name_lwr
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1) * sizeof(WCHAR
));
506 strcpyW(name_lwr
, name
);
508 WideCharToMultiByte(DRIVE_GetCodepage(drive
), 0, name_lwr
, -1,
509 p
, sizeof(buffer
) - strlen(buffer
), NULL
, NULL
);
510 HeapFree(GetProcessHeap(), 0, name_lwr
);
512 file
= fopen( buffer
, "w" );
518 WARN("could not save profile file %s\n", debugstr_w(CurProfile
->dos_name
));
522 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile
->dos_name
), unix_name
);
523 PROFILE_Save( file
, CurProfile
->section
);
525 CurProfile
->changed
= FALSE
;
526 if(!stat(unix_name
,&buf
))
527 CurProfile
->mtime
=buf
.st_mtime
;
532 /***********************************************************************
533 * PROFILE_ReleaseFile
535 * Flush the current profile to disk and remove it from the cache.
537 static void PROFILE_ReleaseFile(void)
540 PROFILE_Free( CurProfile
->section
);
541 if (CurProfile
->dos_name
) HeapFree( GetProcessHeap(), 0, CurProfile
->dos_name
);
542 if (CurProfile
->unix_name
) HeapFree( GetProcessHeap(), 0, CurProfile
->unix_name
);
543 if (CurProfile
->filename
) HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
544 CurProfile
->changed
= FALSE
;
545 CurProfile
->section
= NULL
;
546 CurProfile
->dos_name
= NULL
;
547 CurProfile
->unix_name
= NULL
;
548 CurProfile
->filename
= NULL
;
549 CurProfile
->mtime
= 0;
553 /***********************************************************************
556 * Open a profile file, checking the cached file first.
558 static BOOL
PROFILE_Open( LPCWSTR filename
)
560 DOS_FULL_NAME full_name
;
561 char buffer
[MAX_PATHNAME_LEN
];
563 WCHAR
*name
, *name_lwr
;
568 PROFILE
*tempProfile
;
570 /* First time around */
573 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
575 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
576 if(MRUProfile
[i
] == NULL
) break;
577 MRUProfile
[i
]->changed
=FALSE
;
578 MRUProfile
[i
]->section
=NULL
;
579 MRUProfile
[i
]->dos_name
=NULL
;
580 MRUProfile
[i
]->unix_name
=NULL
;
581 MRUProfile
[i
]->filename
=NULL
;
582 MRUProfile
[i
]->mtime
=0;
585 /* Check for a match */
587 if (strchrW( filename
, '/' ) || strchrW( filename
, '\\' ) ||
588 strchrW( filename
, ':' ))
590 if (!DOSFS_GetFullName( filename
, FALSE
, &full_name
)) return FALSE
;
594 static const WCHAR bkslashW
[] = {'\\',0};
595 WCHAR windirW
[MAX_PATH
];
597 GetWindowsDirectoryW( windirW
, MAX_PATH
);
598 strcatW( windirW
, bkslashW
);
599 strcatW( windirW
, filename
);
600 if (!DOSFS_GetFullName( windirW
, FALSE
, &full_name
)) return FALSE
;
603 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
605 if ((MRUProfile
[i
]->filename
&& !strcmpW( filename
, MRUProfile
[i
]->filename
)) ||
606 (MRUProfile
[i
]->dos_name
&& !strcmpW( full_name
.short_name
, MRUProfile
[i
]->dos_name
)))
611 tempProfile
=MRUProfile
[i
];
613 MRUProfile
[j
]=MRUProfile
[j
-1];
614 CurProfile
=tempProfile
;
616 if(!stat(CurProfile
->unix_name
,&buf
) && CurProfile
->mtime
==buf
.st_mtime
)
617 TRACE("(%s): already opened (mru=%d)\n",
618 debugstr_w(filename
), i
);
620 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
621 debugstr_w(filename
), i
);
626 /* Flush the old current profile */
629 /* Make the oldest profile the current one only in order to get rid of it */
630 if(i
==N_CACHED_PROFILES
)
632 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
633 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
634 MRUProfile
[i
]=MRUProfile
[i
-1];
635 CurProfile
=tempProfile
;
637 if(CurProfile
->filename
) PROFILE_ReleaseFile();
639 /* OK, now that CurProfile is definitely free we assign it our new file */
640 newdos_name
= HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name
.short_name
)+1) * sizeof(WCHAR
) );
641 strcpyW( newdos_name
, full_name
.short_name
);
642 CurProfile
->dos_name
= newdos_name
;
643 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(filename
)+1) * sizeof(WCHAR
) );
644 strcpyW( CurProfile
->filename
, filename
);
646 /* Try to open the profile file, first in $HOME/.wine */
648 /* FIXME: this will need a more general solution */
649 strcpy( buffer
, wine_get_config_dir() );
650 p
= buffer
+ strlen(buffer
);
652 *p
= 0; /* make strlen() below happy */
653 name
= strrchrW( newdos_name
, '\\' ) + 1;
655 /* create a lower cased version of the name */
656 name_lwr
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1) * sizeof(WCHAR
));
657 strcpyW(name_lwr
, name
);
659 WideCharToMultiByte(DRIVE_GetCodepage(full_name
.drive
), 0, name_lwr
, -1,
660 p
, sizeof(buffer
) - strlen(buffer
), NULL
, NULL
);
661 HeapFree(GetProcessHeap(), 0, name_lwr
);
663 if ((file
= fopen( buffer
, "r" )))
665 TRACE("(%s): found it in %s\n", debugstr_w(filename
), buffer
);
666 CurProfile
->unix_name
= HeapAlloc( GetProcessHeap(), 0, strlen(buffer
)+1 );
667 strcpy( CurProfile
->unix_name
, buffer
);
671 CurProfile
->unix_name
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
672 strcpy( CurProfile
->unix_name
, full_name
.long_name
);
673 if ((file
= fopen( full_name
.long_name
, "r" )))
674 TRACE("(%s): found it in %s\n",
675 debugstr_w(filename
), full_name
.long_name
);
680 CurProfile
->section
= PROFILE_Load( file
);
682 if(!stat(CurProfile
->unix_name
,&buf
))
683 CurProfile
->mtime
=buf
.st_mtime
;
687 /* Does not exist yet, we will create it in PROFILE_FlushFile */
688 WARN("profile file %s not found\n", debugstr_w(newdos_name
) );
694 /***********************************************************************
697 * Returns all keys of a section.
698 * If return_values is TRUE, also include the corresponding values.
700 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
701 LPWSTR buffer
, UINT len
, BOOL handle_env
,
706 if(!buffer
) return 0;
708 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
712 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
715 for (key
= section
->key
; key
; key
= key
->next
)
718 if (!*key
->name
) continue; /* Skip empty lines */
719 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
720 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, handle_env
, 0 );
721 len
-= strlenW(buffer
) + 1;
722 buffer
+= strlenW(buffer
) + 1;
725 if (return_values
&& key
->value
) {
727 PROFILE_CopyEntry ( buffer
,
728 key
->value
, len
- 1, handle_env
, 0 );
729 len
-= strlenW(buffer
) + 1;
730 buffer
+= strlenW(buffer
) + 1;
735 /*If either lpszSection or lpszKey is NULL and the supplied
736 destination buffer is too small to hold all the strings,
737 the last string is truncated and followed by two null characters.
738 In this case, the return value is equal to cchReturnBuffer
746 section
= section
->next
;
748 buffer
[0] = buffer
[1] = '\0';
752 /* See GetPrivateProfileSectionNamesA for documentation */
753 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
757 PROFILESECTION
*section
;
768 section
= CurProfile
->section
;
769 while ((section
!=NULL
)) {
770 if (section
->name
[0]) {
771 l
= strlenW(section
->name
)+1;
774 strncpyW(buf
, section
->name
, f
-1);
781 strcpyW(buf
, section
->name
);
785 section
= section
->next
;
792 /***********************************************************************
795 * Get a profile string.
797 * Tests with GetPrivateProfileString16, W95a,
798 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
799 * section key_name def_val res buffer
800 * "set1" "1" "x" 43 [data]
801 * "set1" "1 " "x" 43 [data] (!)
802 * "set1" " 1 "' "x" 43 [data] (!)
803 * "set1" "" "x" 1 "x"
804 * "set1" "" "x " 1 "x" (!)
805 * "set1" "" " x " 3 " x" (!)
806 * "set1" NULL "x" 6 "1\02\03\0\0"
807 * "set1" "" "x" 1 "x"
808 * NULL "1" "x" 0 "" (!)
814 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
815 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
817 PROFILEKEY
*key
= NULL
;
818 static const WCHAR empty_strW
[] = { 0 };
820 if(!buffer
) return 0;
822 if (!def_val
) def_val
= empty_strW
;
827 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
830 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
831 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
833 TRACE("(%s,%s,%s): returning %s\n",
834 debugstr_w(section
), debugstr_w(key_name
),
835 debugstr_w(def_val
), debugstr_w(buffer
) );
836 return strlenW( buffer
);
838 /* no "else" here ! */
839 if (section
&& section
[0])
841 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
, FALSE
);
842 if (!buffer
[0]) /* no luck -> def_val */
844 PROFILE_CopyEntry(buffer
, def_val
, len
, FALSE
, TRUE
);
845 ret
= strlenW(buffer
);
854 /***********************************************************************
857 * Set a profile string.
859 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
860 LPCWSTR value
, BOOL create_always
)
862 if (!key_name
) /* Delete a whole section */
864 TRACE("(%s)\n", debugstr_w(section_name
));
865 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
867 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
868 this is not an error on application's level.*/
870 else if (!value
) /* Delete a key */
872 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
873 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
874 section_name
, key_name
);
875 return TRUE
; /* same error handling as above */
877 else /* Set the key value */
879 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
880 key_name
, TRUE
, create_always
);
881 TRACE("(%s,%s,%s):\n",
882 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
883 if (!key
) return FALSE
;
886 /* strip the leading spaces. We can safely strip \n\r and
887 * friends too, they should not happen here anyway. */
888 while (PROFILE_isspace(*value
)) value
++;
890 if (!strcmpW( key
->value
, value
))
892 TRACE(" no change needed\n" );
893 return TRUE
; /* No change needed */
895 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
896 HeapFree( GetProcessHeap(), 0, key
->value
);
898 else TRACE(" creating key\n" );
899 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
900 strcpyW( key
->value
, value
);
901 CurProfile
->changed
= TRUE
;
907 /***********************************************************************
908 * PROFILE_UsageWineIni
910 * Explain the wine.ini file to those who don't read documentation.
911 * Keep below one screenful in length so that error messages above are
914 void PROFILE_UsageWineIni(void)
916 MESSAGE("Perhaps you have not properly edited or created "
917 "your Wine configuration file,\n");
918 MESSAGE("which is (supposed to be) '%s/config'.\n", wine_get_config_dir());
922 /********************* API functions **********************************/
924 /***********************************************************************
925 * GetProfileInt (KERNEL.57)
927 UINT16 WINAPI
GetProfileInt16( LPCSTR section
, LPCSTR entry
, INT16 def_val
)
929 return GetPrivateProfileInt16( section
, entry
, def_val
, "win.ini" );
933 /***********************************************************************
934 * GetProfileIntA (KERNEL32.@)
936 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
938 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
941 /***********************************************************************
942 * GetProfileIntW (KERNEL32.@)
944 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
946 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
950 * if allow_section_name_copy is TRUE, allow the copying :
951 * - of Section names if 'section' is NULL
952 * - of Keys in a Section if 'entry' is NULL
953 * (see MSDN doc for GetPrivateProfileString)
955 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
956 LPCWSTR def_val
, LPWSTR buffer
,
957 UINT len
, LPCWSTR filename
,
958 BOOL allow_section_name_copy
)
961 LPWSTR pDefVal
= NULL
;
966 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
967 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
969 /* strip any trailing ' ' of def_val. */
972 LPCWSTR p
= &def_val
[strlenW(def_val
)]; /* even "" works ! */
980 if (*p
== ' ') /* ouch, contained trailing ' ' */
982 int len
= (int)(p
- def_val
);
983 pDefVal
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
984 strncpyW(pDefVal
, def_val
, len
);
989 pDefVal
= (LPWSTR
)def_val
;
991 RtlEnterCriticalSection( &PROFILE_CritSect
);
993 if (PROFILE_Open( filename
)) {
994 if ((allow_section_name_copy
) && (section
== NULL
))
995 ret
= PROFILE_GetSectionNames(buffer
, len
);
997 /* PROFILE_GetString already handles the 'entry == NULL' case */
998 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1000 lstrcpynW( buffer
, pDefVal
, len
);
1001 ret
= strlenW( buffer
);
1004 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1006 if (pDefVal
!= def_val
) /* allocated */
1007 HeapFree(GetProcessHeap(), 0, pDefVal
);
1009 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1014 /***********************************************************************
1015 * GetPrivateProfileString (KERNEL.128)
1017 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1018 LPCSTR def_val
, LPSTR buffer
,
1019 UINT16 len
, LPCSTR filename
)
1021 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1023 INT16 retW
, ret
= 0;
1025 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1026 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1027 else sectionW
.Buffer
= NULL
;
1028 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1029 else entryW
.Buffer
= NULL
;
1030 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1031 else def_valW
.Buffer
= NULL
;
1032 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1033 else filenameW
.Buffer
= NULL
;
1035 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1036 def_valW
.Buffer
, bufferW
, len
,
1037 filenameW
.Buffer
, FALSE
);
1040 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1047 ret
--; /* strip terminating 0 */
1050 RtlFreeUnicodeString(§ionW
);
1051 RtlFreeUnicodeString(&entryW
);
1052 RtlFreeUnicodeString(&def_valW
);
1053 RtlFreeUnicodeString(&filenameW
);
1054 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1058 /***********************************************************************
1059 * GetPrivateProfileStringA (KERNEL32.@)
1061 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1062 LPCSTR def_val
, LPSTR buffer
,
1063 UINT len
, LPCSTR filename
)
1065 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1069 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1070 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1071 else sectionW
.Buffer
= NULL
;
1072 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1073 else entryW
.Buffer
= NULL
;
1074 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1075 else def_valW
.Buffer
= NULL
;
1076 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1077 else filenameW
.Buffer
= NULL
;
1079 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1080 def_valW
.Buffer
, bufferW
, len
,
1084 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1091 ret
--; /* strip terminating 0 */
1094 RtlFreeUnicodeString(§ionW
);
1095 RtlFreeUnicodeString(&entryW
);
1096 RtlFreeUnicodeString(&def_valW
);
1097 RtlFreeUnicodeString(&filenameW
);
1098 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1102 /***********************************************************************
1103 * GetPrivateProfileStringW (KERNEL32.@)
1105 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1106 LPCWSTR def_val
, LPWSTR buffer
,
1107 UINT len
, LPCWSTR filename
)
1109 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1110 buffer
, len
, filename
, TRUE
);
1113 /***********************************************************************
1114 * GetProfileString (KERNEL.58)
1116 INT16 WINAPI
GetProfileString16( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1117 LPSTR buffer
, UINT16 len
)
1119 return GetPrivateProfileString16( section
, entry
, def_val
,
1120 buffer
, len
, "win.ini" );
1123 /***********************************************************************
1124 * GetProfileStringA (KERNEL32.@)
1126 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1127 LPSTR buffer
, UINT len
)
1129 return GetPrivateProfileStringA( section
, entry
, def_val
,
1130 buffer
, len
, "win.ini" );
1133 /***********************************************************************
1134 * GetProfileStringW (KERNEL32.@)
1136 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1137 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1139 return GetPrivateProfileStringW( section
, entry
, def_val
,
1140 buffer
, len
, wininiW
);
1143 /***********************************************************************
1144 * WriteProfileString (KERNEL.59)
1146 BOOL16 WINAPI
WriteProfileString16( LPCSTR section
, LPCSTR entry
,
1149 return WritePrivateProfileString16( section
, entry
, string
, "win.ini" );
1152 /***********************************************************************
1153 * WriteProfileStringA (KERNEL32.@)
1155 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1158 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1161 /***********************************************************************
1162 * WriteProfileStringW (KERNEL32.@)
1164 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1167 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1171 /***********************************************************************
1172 * GetPrivateProfileInt (KERNEL.127)
1174 UINT16 WINAPI
GetPrivateProfileInt16( LPCSTR section
, LPCSTR entry
,
1175 INT16 def_val
, LPCSTR filename
)
1177 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1178 * here, but Win98SE doesn't care about this at all, so I deleted it.
1179 * AFAIR versions prior to Win9x had these limits, though. */
1180 return (INT16
)GetPrivateProfileIntA(section
,entry
,def_val
,filename
);
1183 /***********************************************************************
1184 * GetPrivateProfileIntW (KERNEL32.@)
1186 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1187 INT def_val
, LPCWSTR filename
)
1190 UNICODE_STRING bufferW
;
1194 if (!(len
= GetPrivateProfileStringW( section
, entry
, emptystringW
,
1195 buffer
, sizeof(buffer
)/sizeof(WCHAR
),
1199 if (len
+1 == sizeof(buffer
)/sizeof(WCHAR
)) FIXME("result may be wrong!\n");
1201 /* FIXME: if entry can be found but it's empty, then Win16 is
1202 * supposed to return 0 instead of def_val ! Difficult/problematic
1203 * to implement (every other failure also returns zero buffer),
1204 * thus wait until testing framework avail for making sure nothing
1205 * else gets broken that way. */
1206 if (!buffer
[0]) return (UINT
)def_val
;
1208 RtlInitUnicodeString( &bufferW
, buffer
);
1209 RtlUnicodeStringToInteger( &bufferW
, 10, &result
);
1213 /***********************************************************************
1214 * GetPrivateProfileIntA (KERNEL32.@)
1216 * FIXME: rewrite using unicode
1218 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1219 INT def_val
, LPCSTR filename
)
1221 UNICODE_STRING entryW
, filenameW
, sectionW
;
1223 if(entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1224 else entryW
.Buffer
= NULL
;
1225 if(filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1226 else filenameW
.Buffer
= NULL
;
1227 if(section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1228 else sectionW
.Buffer
= NULL
;
1229 res
= GetPrivateProfileIntW(sectionW
.Buffer
, entryW
.Buffer
, def_val
,
1231 RtlFreeUnicodeString(§ionW
);
1232 RtlFreeUnicodeString(&filenameW
);
1233 RtlFreeUnicodeString(&entryW
);
1237 /***********************************************************************
1238 * GetPrivateProfileSection (KERNEL.418)
1240 INT16 WINAPI
GetPrivateProfileSection16( LPCSTR section
, LPSTR buffer
,
1241 UINT16 len
, LPCSTR filename
)
1243 return GetPrivateProfileSectionA( section
, buffer
, len
, filename
);
1246 /***********************************************************************
1247 * GetPrivateProfileSectionW (KERNEL32.@)
1249 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1250 DWORD len
, LPCWSTR filename
)
1254 RtlEnterCriticalSection( &PROFILE_CritSect
);
1256 if (PROFILE_Open( filename
))
1257 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
,
1260 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1265 /***********************************************************************
1266 * GetPrivateProfileSectionA (KERNEL32.@)
1268 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1269 DWORD len
, LPCSTR filename
)
1271 UNICODE_STRING sectionW
, filenameW
;
1275 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1276 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1277 else sectionW
.Buffer
= NULL
;
1278 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1279 else filenameW
.Buffer
= NULL
;
1281 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1284 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1300 RtlFreeUnicodeString(§ionW
);
1301 RtlFreeUnicodeString(&filenameW
);
1302 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1306 /***********************************************************************
1307 * GetProfileSection (KERNEL.419)
1309 INT16 WINAPI
GetProfileSection16( LPCSTR section
, LPSTR buffer
, UINT16 len
)
1311 return GetPrivateProfileSection16( section
, buffer
, len
, "win.ini" );
1314 /***********************************************************************
1315 * GetProfileSectionA (KERNEL32.@)
1317 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1319 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1322 /***********************************************************************
1323 * GetProfileSectionW (KERNEL32.@)
1325 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1327 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1331 /***********************************************************************
1332 * WritePrivateProfileString (KERNEL.129)
1334 BOOL16 WINAPI
WritePrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1335 LPCSTR string
, LPCSTR filename
)
1337 return WritePrivateProfileStringA(section
,entry
,string
,filename
);
1340 /***********************************************************************
1341 * WritePrivateProfileStringW (KERNEL32.@)
1343 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1344 LPCWSTR string
, LPCWSTR filename
)
1348 RtlEnterCriticalSection( &PROFILE_CritSect
);
1350 if (PROFILE_Open( filename
))
1352 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1354 PROFILE_FlushFile();
1355 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1359 FIXME("(NULL?,%s,%s,%s)?\n",
1360 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1362 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1363 PROFILE_FlushFile();
1368 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1372 /***********************************************************************
1373 * WritePrivateProfileStringA (KERNEL32.@)
1375 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1376 LPCSTR string
, LPCSTR filename
)
1378 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1381 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1382 else sectionW
.Buffer
= NULL
;
1383 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1384 else entryW
.Buffer
= NULL
;
1385 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1386 else stringW
.Buffer
= NULL
;
1387 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1388 else filenameW
.Buffer
= NULL
;
1390 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1391 stringW
.Buffer
, filenameW
.Buffer
);
1392 RtlFreeUnicodeString(§ionW
);
1393 RtlFreeUnicodeString(&entryW
);
1394 RtlFreeUnicodeString(&stringW
);
1395 RtlFreeUnicodeString(&filenameW
);
1399 /***********************************************************************
1400 * WritePrivateProfileSection (KERNEL.416)
1402 BOOL16 WINAPI
WritePrivateProfileSection16( LPCSTR section
,
1403 LPCSTR string
, LPCSTR filename
)
1405 return WritePrivateProfileSectionA( section
, string
, filename
);
1408 /***********************************************************************
1409 * WritePrivateProfileSectionW (KERNEL32.@)
1411 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1412 LPCWSTR string
, LPCWSTR filename
)
1417 RtlEnterCriticalSection( &PROFILE_CritSect
);
1419 if (PROFILE_Open( filename
)) {
1420 if (!section
&& !string
)
1421 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1422 else if (!string
) {/* delete the named section*/
1423 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1424 PROFILE_FlushFile();
1426 PROFILE_DeleteAllKeys(section
);
1429 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1430 strcpyW( buf
, string
);
1431 if((p
= strchrW( buf
, '='))) {
1433 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1435 HeapFree( GetProcessHeap(), 0, buf
);
1436 string
+= strlenW(string
)+1;
1438 PROFILE_FlushFile();
1442 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1446 /***********************************************************************
1447 * WritePrivateProfileSectionA (KERNEL32.@)
1449 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1450 LPCSTR string
, LPCSTR filename
)
1453 UNICODE_STRING sectionW
, filenameW
;
1462 while(*p
) p
+= strlen(p
) + 1;
1463 lenA
= p
- string
+ 1;
1464 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1465 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1466 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1468 else stringW
= NULL
;
1469 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1470 else sectionW
.Buffer
= NULL
;
1471 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1472 else filenameW
.Buffer
= NULL
;
1474 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1476 HeapFree(GetProcessHeap(), 0, stringW
);
1477 RtlFreeUnicodeString(§ionW
);
1478 RtlFreeUnicodeString(&filenameW
);
1482 /***********************************************************************
1483 * WriteProfileSection (KERNEL.417)
1485 BOOL16 WINAPI
WriteProfileSection16( LPCSTR section
, LPCSTR keys_n_values
)
1487 return WritePrivateProfileSection16( section
, keys_n_values
, "win.ini");
1490 /***********************************************************************
1491 * WriteProfileSectionA (KERNEL32.@)
1493 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1496 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1499 /***********************************************************************
1500 * WriteProfileSectionW (KERNEL32.@)
1502 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1504 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1507 /***********************************************************************
1508 * GetPrivateProfileSectionNames (KERNEL.143)
1510 WORD WINAPI
GetPrivateProfileSectionNames16( LPSTR buffer
, WORD size
,
1513 return GetPrivateProfileSectionNamesA(buffer
,size
,filename
);
1517 /***********************************************************************
1518 * GetProfileSectionNames (KERNEL.142)
1520 WORD WINAPI
GetProfileSectionNames16(LPSTR buffer
, WORD size
)
1523 return GetPrivateProfileSectionNamesA(buffer
,size
,"win.ini");
1527 /***********************************************************************
1528 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1530 * Returns the section names contained in the specified file.
1531 * FIXME: Where do we find this file when the path is relative?
1532 * The section names are returned as a list of strings with an extra
1533 * '\0' to mark the end of the list. Except for that the behavior
1534 * depends on the Windows version.
1537 * - if the buffer is 0 or 1 character long then it is as if it was of
1539 * - otherwise, if the buffer is to small only the section names that fit
1541 * - note that this means if the buffer was to small to return even just
1542 * the first section name then a single '\0' will be returned.
1543 * - the return value is the number of characters written in the buffer,
1544 * except if the buffer was too smal in which case len-2 is returned
1547 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1548 * '\0' and the return value is 0
1549 * - otherwise if the buffer is too small then the first section name that
1550 * does not fit is truncated so that the string list can be terminated
1551 * correctly (double '\0')
1552 * - the return value is the number of characters written in the buffer
1553 * except for the trailing '\0'. If the buffer is too small, then the
1554 * return value is len-2
1555 * - Win2000 has a bug that triggers when the section names and the
1556 * trailing '\0' fit exactly in the buffer. In that case the trailing
1559 * Wine implements the observed Win2000 behavior (except for the bug).
1561 * Note that when the buffer is big enough then the return value may be any
1562 * value between 1 and len-1 (or len in Win95), including len-2.
1564 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1569 RtlEnterCriticalSection( &PROFILE_CritSect
);
1571 if (PROFILE_Open( filename
))
1572 ret
= PROFILE_GetSectionNames(buffer
, size
);
1574 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1580 /***********************************************************************
1581 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1583 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1586 UNICODE_STRING filenameW
;
1590 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1591 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1592 else filenameW
.Buffer
= NULL
;
1594 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1597 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1605 RtlFreeUnicodeString(&filenameW
);
1606 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1610 /***********************************************************************
1611 * GetPrivateProfileStruct (KERNEL.407)
1613 BOOL16 WINAPI
GetPrivateProfileStruct16(LPCSTR section
, LPCSTR key
,
1614 LPVOID buf
, UINT16 len
, LPCSTR filename
)
1616 return GetPrivateProfileStructA( section
, key
, buf
, len
, filename
);
1619 /***********************************************************************
1620 * GetPrivateProfileStructW (KERNEL32.@)
1622 * Should match Win95's behaviour pretty much
1624 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1625 LPVOID buf
, UINT len
, LPCWSTR filename
)
1629 RtlEnterCriticalSection( &PROFILE_CritSect
);
1631 if (PROFILE_Open( filename
)) {
1632 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1634 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1635 if (((strlenW(k
->value
) - 2) / 2) == len
)
1642 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1643 /* check for invalid chars in ASCII coded hex string */
1644 for (p
=k
->value
; p
< end
; p
++)
1648 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1649 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1656 BOOL highnibble
= TRUE
;
1658 LPBYTE binbuf
= (LPBYTE
)buf
;
1660 end
-= 2; /* don't include checksum in output data */
1661 /* translate ASCII hex format into binary data */
1662 for (p
=k
->value
; p
< end
; p
++)
1666 (c
- 'A' + 10) : (c
- '0');
1673 *binbuf
++ = b
; /* feed binary data into output */
1674 chksum
+= b
; /* calculate checksum */
1676 highnibble
^= 1; /* toggle */
1678 /* retrieve stored checksum value */
1680 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1682 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1683 if (b
== (chksum
& 0xff)) /* checksums match ? */
1689 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1694 /***********************************************************************
1695 * GetPrivateProfileStructA (KERNEL32.@)
1697 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1698 LPVOID buffer
, UINT len
, LPCSTR filename
)
1700 UNICODE_STRING sectionW
, keyW
, filenameW
;
1703 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1704 else sectionW
.Buffer
= NULL
;
1705 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1706 else keyW
.Buffer
= NULL
;
1707 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1708 else filenameW
.Buffer
= NULL
;
1710 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1712 /* Do not translate binary data. */
1714 RtlFreeUnicodeString(§ionW
);
1715 RtlFreeUnicodeString(&keyW
);
1716 RtlFreeUnicodeString(&filenameW
);
1722 /***********************************************************************
1723 * WritePrivateProfileStruct (KERNEL.406)
1725 BOOL16 WINAPI
WritePrivateProfileStruct16 (LPCSTR section
, LPCSTR key
,
1726 LPVOID buf
, UINT16 bufsize
, LPCSTR filename
)
1728 return WritePrivateProfileStructA( section
, key
, buf
, bufsize
, filename
);
1731 /***********************************************************************
1732 * WritePrivateProfileStructW (KERNEL32.@)
1734 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1735 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1739 LPWSTR outstring
, p
;
1742 if (!section
&& !key
&& !buf
) /* flush the cache */
1743 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1745 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1746 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1748 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1749 *p
++ = hex
[*binbuf
>> 4];
1750 *p
++ = hex
[*binbuf
& 0xf];
1753 /* checksum is sum & 0xff */
1754 *p
++ = hex
[(sum
& 0xf0) >> 4];
1755 *p
++ = hex
[sum
& 0xf];
1758 RtlEnterCriticalSection( &PROFILE_CritSect
);
1760 if (PROFILE_Open( filename
)) {
1761 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1762 PROFILE_FlushFile();
1765 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1767 HeapFree( GetProcessHeap(), 0, outstring
);
1772 /***********************************************************************
1773 * WritePrivateProfileStructA (KERNEL32.@)
1775 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1776 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1778 UNICODE_STRING sectionW
, keyW
, filenameW
;
1781 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1782 else sectionW
.Buffer
= NULL
;
1783 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1784 else keyW
.Buffer
= NULL
;
1785 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1786 else filenameW
.Buffer
= NULL
;
1788 /* Do not translate binary data. */
1789 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1792 RtlFreeUnicodeString(§ionW
);
1793 RtlFreeUnicodeString(&keyW
);
1794 RtlFreeUnicodeString(&filenameW
);
1799 /***********************************************************************
1800 * WriteOutProfiles (KERNEL.315)
1802 void WINAPI
WriteOutProfiles16(void)
1804 RtlEnterCriticalSection( &PROFILE_CritSect
);
1805 PROFILE_FlushFile();
1806 RtlLeaveCriticalSection( &PROFILE_CritSect
);
1809 /***********************************************************************
1810 * CloseProfileUserMapping (KERNEL32.@)
1812 BOOL WINAPI
CloseProfileUserMapping(void) {
1813 FIXME("(), stub!\n");
1814 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);