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 #define PROFILE_MAX_LINE_LEN 1024
88 /* Check for comments in profile */
89 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
91 static const WCHAR wininiW
[] = { 'w','i','n','.','i','n','i',0 };
93 static CRITICAL_SECTION PROFILE_CritSect
= CRITICAL_SECTION_INIT("PROFILE_CritSect");
95 static const char hex
[16] = "0123456789ABCDEF";
97 /***********************************************************************
100 * Copy the content of an entry into a buffer, removing quotes, and possibly
101 * translating environment variables.
103 static void PROFILE_CopyEntry( LPWSTR buffer
, LPCWSTR value
, int len
,
104 int handle_env
, BOOL strip_quote
)
111 if (strip_quote
&& ((*value
== '\'') || (*value
== '\"')))
113 if (value
[1] && (value
[strlenW(value
)-1] == *value
)) quote
= *value
++;
118 lstrcpynW( buffer
, value
, len
);
119 if (quote
&& (len
>= strlenW(value
))) buffer
[strlenW(buffer
)-1] = '\0';
123 for (p
= value
; (*p
&& (len
> 1)); *buffer
++ = *p
++, len
-- )
125 if ((*p
== '$') && (p
[1] == '{'))
128 LPCWSTR p2
= strchrW( p
, '}' );
130 if (!p2
) continue; /* ignore it */
131 copy_len
= min( 1024, (int)(p2
-p
)-1 );
132 strncpyW(env_val
, p
+ 2, copy_len
);
133 env_val
[copy_len
- 1] = 0; /* ensure 0 termination */
135 if (GetEnvironmentVariableW( env_val
, buffer
, len
))
137 copy_len
= strlenW( buffer
);
144 if (quote
&& (len
> 1)) buffer
--;
149 /***********************************************************************
152 * Save a profile tree to a file.
154 static void PROFILE_Save( FILE *file
, PROFILESECTION
*section
)
157 char buffer
[PROFILE_MAX_LINE_LEN
];
159 for ( ; section
; section
= section
->next
)
161 if (section
->name
[0])
163 WideCharToMultiByte(CP_ACP
, 0, section
->name
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
164 fprintf( file
, "\r\n[%s]\r\n", buffer
);
166 for (key
= section
->key
; key
; key
= key
->next
)
168 WideCharToMultiByte(CP_ACP
, 0, key
->name
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
169 fprintf( file
, "%s", buffer
);
172 WideCharToMultiByte(CP_ACP
, 0, key
->value
, -1, buffer
, sizeof(buffer
), NULL
, NULL
);
173 fprintf( file
, "=%s", buffer
);
175 fprintf( file
, "\r\n" );
181 /***********************************************************************
184 * Free a profile tree.
186 static void PROFILE_Free( PROFILESECTION
*section
)
188 PROFILESECTION
*next_section
;
189 PROFILEKEY
*key
, *next_key
;
191 for ( ; section
; section
= next_section
)
193 for (key
= section
->key
; key
; key
= next_key
)
195 next_key
= key
->next
;
196 if (key
->value
) HeapFree( GetProcessHeap(), 0, key
->value
);
197 HeapFree( GetProcessHeap(), 0, key
);
199 next_section
= section
->next
;
200 HeapFree( GetProcessHeap(), 0, section
);
204 static inline int PROFILE_isspace(char c
)
206 if (isspace(c
)) return 1;
207 if (c
=='\r' || c
==0x1a) return 1;
208 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
213 /***********************************************************************
216 * Load a profile tree from a file.
218 static PROFILESECTION
*PROFILE_Load( FILE *file
)
220 char buffer
[PROFILE_MAX_LINE_LEN
];
223 PROFILESECTION
*section
, *first_section
;
224 PROFILESECTION
**next_section
;
225 PROFILEKEY
*key
, *prev_key
, **next_key
;
227 first_section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) );
228 if(first_section
== NULL
) return NULL
;
229 first_section
->name
[0] = 0;
230 first_section
->key
= NULL
;
231 first_section
->next
= NULL
;
232 next_section
= &first_section
->next
;
233 next_key
= &first_section
->key
;
236 while (fgets( buffer
, PROFILE_MAX_LINE_LEN
, file
))
240 while (*p
&& PROFILE_isspace(*p
)) p
++;
241 if (*p
== '[') /* section start */
243 if (!(p2
= strrchr( p
, ']' )))
245 WARN("Invalid section header at line %d: '%s'\n",
253 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) + len
* sizeof(WCHAR
) )))
255 MultiByteToWideChar(CP_ACP
, 0, p
, -1, section
->name
, len
+ 1);
257 section
->next
= NULL
;
258 *next_section
= section
;
259 next_section
= §ion
->next
;
260 next_key
= §ion
->key
;
263 TRACE("New section: %s\n", debugstr_w(section
->name
));
270 while ((p2
> p
) && ((*p2
== '\n') || PROFILE_isspace(*p2
))) *p2
--='\0';
272 if ((p2
= strchr( p
, '=' )) != NULL
)
275 while ((p3
> p
) && PROFILE_isspace(*p3
)) *p3
-- = '\0';
277 while (*p2
&& PROFILE_isspace(*p2
)) p2
++;
280 if(*p
|| !prev_key
|| *prev_key
->name
)
283 if (!(key
= HeapAlloc( GetProcessHeap(), 0, sizeof(*key
) + len
* sizeof(WCHAR
) ))) break;
284 MultiByteToWideChar(CP_ACP
, 0, p
, -1, key
->name
, len
+ 1);
287 len
= strlen(p2
) + 1;
288 key
->value
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
289 MultiByteToWideChar(CP_ACP
, 0, p2
, -1, key
->value
, len
);
291 else key
->value
= NULL
;
295 next_key
= &key
->next
;
298 TRACE("New key: name=%s, value=%s\n",
299 debugstr_w(key
->name
), key
->value
? debugstr_w(key
->value
) : "(none)");
302 return first_section
;
306 /***********************************************************************
307 * PROFILE_DeleteSection
309 * Delete a section from a profile tree.
311 static BOOL
PROFILE_DeleteSection( PROFILESECTION
**section
, LPCWSTR name
)
315 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, name
))
317 PROFILESECTION
*to_del
= *section
;
318 *section
= to_del
->next
;
320 PROFILE_Free( to_del
);
323 section
= &(*section
)->next
;
329 /***********************************************************************
332 * Delete a key from a profile tree.
334 static BOOL
PROFILE_DeleteKey( PROFILESECTION
**section
,
335 LPCWSTR section_name
, LPCWSTR key_name
)
339 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
341 PROFILEKEY
**key
= &(*section
)->key
;
344 if (!strcmpiW( (*key
)->name
, key_name
))
346 PROFILEKEY
*to_del
= *key
;
348 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
349 HeapFree( GetProcessHeap(), 0, to_del
);
355 section
= &(*section
)->next
;
361 /***********************************************************************
362 * PROFILE_DeleteAllKeys
364 * Delete all keys from a profile tree.
366 void PROFILE_DeleteAllKeys( LPCWSTR section_name
)
368 PROFILESECTION
**section
= &CurProfile
->section
;
371 if ((*section
)->name
[0] && !strcmpiW( (*section
)->name
, section_name
))
373 PROFILEKEY
**key
= &(*section
)->key
;
376 PROFILEKEY
*to_del
= *key
;
378 if (to_del
->value
) HeapFree( GetProcessHeap(), 0, to_del
->value
);
379 HeapFree( GetProcessHeap(), 0, to_del
);
380 CurProfile
->changed
=TRUE
;
383 section
= &(*section
)->next
;
388 /***********************************************************************
391 * Find a key in a profile tree, optionally creating it.
393 static PROFILEKEY
*PROFILE_Find( PROFILESECTION
**section
, LPCWSTR section_name
,
394 LPCWSTR key_name
, BOOL create
, BOOL create_always
)
399 while (PROFILE_isspace(*section_name
)) section_name
++;
400 p
= section_name
+ strlenW(section_name
) - 1;
401 while ((p
> section_name
) && PROFILE_isspace(*p
)) p
--;
402 seclen
= p
- section_name
+ 1;
404 while (PROFILE_isspace(*key_name
)) key_name
++;
405 p
= key_name
+ strlenW(key_name
) - 1;
406 while ((p
> key_name
) && PROFILE_isspace(*p
)) p
--;
407 keylen
= p
- key_name
+ 1;
411 if ( ((*section
)->name
[0])
412 && (!(strncmpiW( (*section
)->name
, section_name
, seclen
)))
413 && (((*section
)->name
)[seclen
] == '\0') )
415 PROFILEKEY
**key
= &(*section
)->key
;
419 /* If create_always is FALSE then we check if the keyname already exists.
420 * Otherwise we add it regardless of its existence, to allow
421 * keys to be added more then once in some cases.
425 if ( (!(strncmpiW( (*key
)->name
, key_name
, keylen
)))
426 && (((*key
)->name
)[keylen
] == '\0') )
431 if (!create
) return NULL
;
432 if (!(*key
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
434 strcpyW( (*key
)->name
, key_name
);
435 (*key
)->value
= NULL
;
439 section
= &(*section
)->next
;
441 if (!create
) return NULL
;
442 *section
= HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION
) + strlenW(section_name
) * sizeof(WCHAR
) );
443 if(*section
== NULL
) return NULL
;
444 strcpyW( (*section
)->name
, section_name
);
445 (*section
)->next
= NULL
;
446 if (!((*section
)->key
= HeapAlloc( GetProcessHeap(), 0,
447 sizeof(PROFILEKEY
) + strlenW(key_name
) * sizeof(WCHAR
) )))
449 HeapFree(GetProcessHeap(), 0, *section
);
452 strcpyW( (*section
)->key
->name
, key_name
);
453 (*section
)->key
->value
= NULL
;
454 (*section
)->key
->next
= NULL
;
455 return (*section
)->key
;
459 /***********************************************************************
462 * Flush the current profile to disk if changed.
464 static BOOL
PROFILE_FlushFile(void)
466 char *p
, buffer
[MAX_PATHNAME_LEN
];
467 const char *unix_name
;
473 WARN("No current profile!\n");
477 if (!CurProfile
->changed
|| !CurProfile
->dos_name
) return TRUE
;
478 if (!(unix_name
= CurProfile
->unix_name
) || !(file
= fopen(unix_name
, "w")))
480 int drive
= toupperW(CurProfile
->dos_name
[0]) - 'A';
481 WCHAR
*name
, *name_lwr
;
482 /* Try to create it in $HOME/.wine */
483 /* FIXME: this will need a more general solution */
484 strcpy( buffer
, wine_get_config_dir() );
485 p
= buffer
+ strlen(buffer
);
487 *p
= 0; /* make strlen() below happy */
488 name
= strrchrW( CurProfile
->dos_name
, '\\' ) + 1;
490 /* create a lower cased version of the name */
491 name_lwr
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1) * sizeof(WCHAR
));
492 strcpyW(name_lwr
, name
);
494 WideCharToMultiByte(DRIVE_GetCodepage(drive
), 0, name_lwr
, -1,
495 p
, sizeof(buffer
) - strlen(buffer
), NULL
, NULL
);
496 HeapFree(GetProcessHeap(), 0, name_lwr
);
498 file
= fopen( buffer
, "w" );
504 WARN("could not save profile file %s\n", debugstr_w(CurProfile
->dos_name
));
508 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile
->dos_name
), unix_name
);
509 PROFILE_Save( file
, CurProfile
->section
);
511 CurProfile
->changed
= FALSE
;
512 if(!stat(unix_name
,&buf
))
513 CurProfile
->mtime
=buf
.st_mtime
;
518 /***********************************************************************
519 * PROFILE_ReleaseFile
521 * Flush the current profile to disk and remove it from the cache.
523 static void PROFILE_ReleaseFile(void)
526 PROFILE_Free( CurProfile
->section
);
527 if (CurProfile
->dos_name
) HeapFree( GetProcessHeap(), 0, CurProfile
->dos_name
);
528 if (CurProfile
->unix_name
) HeapFree( GetProcessHeap(), 0, CurProfile
->unix_name
);
529 if (CurProfile
->filename
) HeapFree( GetProcessHeap(), 0, CurProfile
->filename
);
530 CurProfile
->changed
= FALSE
;
531 CurProfile
->section
= NULL
;
532 CurProfile
->dos_name
= NULL
;
533 CurProfile
->unix_name
= NULL
;
534 CurProfile
->filename
= NULL
;
535 CurProfile
->mtime
= 0;
539 /***********************************************************************
542 * Open a profile file, checking the cached file first.
544 static BOOL
PROFILE_Open( LPCWSTR filename
)
546 DOS_FULL_NAME full_name
;
547 char buffer
[MAX_PATHNAME_LEN
];
549 WCHAR
*name
, *name_lwr
;
554 PROFILE
*tempProfile
;
556 /* First time around */
559 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
561 MRUProfile
[i
]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE
) );
562 if(MRUProfile
[i
] == NULL
) break;
563 MRUProfile
[i
]->changed
=FALSE
;
564 MRUProfile
[i
]->section
=NULL
;
565 MRUProfile
[i
]->dos_name
=NULL
;
566 MRUProfile
[i
]->unix_name
=NULL
;
567 MRUProfile
[i
]->filename
=NULL
;
568 MRUProfile
[i
]->mtime
=0;
571 /* Check for a match */
573 if (strchrW( filename
, '/' ) || strchrW( filename
, '\\' ) ||
574 strchrW( filename
, ':' ))
576 if (!DOSFS_GetFullName( filename
, FALSE
, &full_name
)) return FALSE
;
580 static const WCHAR bkslashW
[] = {'\\',0};
581 WCHAR windirW
[MAX_PATH
];
583 GetWindowsDirectoryW( windirW
, MAX_PATH
);
584 strcatW( windirW
, bkslashW
);
585 strcatW( windirW
, filename
);
586 if (!DOSFS_GetFullName( windirW
, FALSE
, &full_name
)) return FALSE
;
589 for(i
=0;i
<N_CACHED_PROFILES
;i
++)
591 if ((MRUProfile
[i
]->filename
&& !strcmpW( filename
, MRUProfile
[i
]->filename
)) ||
592 (MRUProfile
[i
]->dos_name
&& !strcmpW( full_name
.short_name
, MRUProfile
[i
]->dos_name
)))
597 tempProfile
=MRUProfile
[i
];
599 MRUProfile
[j
]=MRUProfile
[j
-1];
600 CurProfile
=tempProfile
;
602 if(!stat(CurProfile
->unix_name
,&buf
) && CurProfile
->mtime
==buf
.st_mtime
)
603 TRACE("(%s): already opened (mru=%d)\n",
604 debugstr_w(filename
), i
);
606 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
607 debugstr_w(filename
), i
);
612 /* Flush the old current profile */
615 /* Make the oldest profile the current one only in order to get rid of it */
616 if(i
==N_CACHED_PROFILES
)
618 tempProfile
=MRUProfile
[N_CACHED_PROFILES
-1];
619 for(i
=N_CACHED_PROFILES
-1;i
>0;i
--)
620 MRUProfile
[i
]=MRUProfile
[i
-1];
621 CurProfile
=tempProfile
;
623 if(CurProfile
->filename
) PROFILE_ReleaseFile();
625 /* OK, now that CurProfile is definitely free we assign it our new file */
626 newdos_name
= HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name
.short_name
)+1) * sizeof(WCHAR
) );
627 strcpyW( newdos_name
, full_name
.short_name
);
628 CurProfile
->dos_name
= newdos_name
;
629 CurProfile
->filename
= HeapAlloc( GetProcessHeap(), 0, (strlenW(filename
)+1) * sizeof(WCHAR
) );
630 strcpyW( CurProfile
->filename
, filename
);
632 /* Try to open the profile file, first in $HOME/.wine */
634 /* FIXME: this will need a more general solution */
635 strcpy( buffer
, wine_get_config_dir() );
636 p
= buffer
+ strlen(buffer
);
638 *p
= 0; /* make strlen() below happy */
639 name
= strrchrW( newdos_name
, '\\' ) + 1;
641 /* create a lower cased version of the name */
642 name_lwr
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1) * sizeof(WCHAR
));
643 strcpyW(name_lwr
, name
);
645 WideCharToMultiByte(DRIVE_GetCodepage(full_name
.drive
), 0, name_lwr
, -1,
646 p
, sizeof(buffer
) - strlen(buffer
), NULL
, NULL
);
647 HeapFree(GetProcessHeap(), 0, name_lwr
);
649 if ((file
= fopen( buffer
, "r" )))
651 TRACE("(%s): found it in %s\n", debugstr_w(filename
), buffer
);
652 CurProfile
->unix_name
= HeapAlloc( GetProcessHeap(), 0, strlen(buffer
)+1 );
653 strcpy( CurProfile
->unix_name
, buffer
);
657 CurProfile
->unix_name
= HeapAlloc( GetProcessHeap(), 0, strlen(full_name
.long_name
)+1 );
658 strcpy( CurProfile
->unix_name
, full_name
.long_name
);
659 if ((file
= fopen( full_name
.long_name
, "r" )))
660 TRACE("(%s): found it in %s\n",
661 debugstr_w(filename
), full_name
.long_name
);
666 CurProfile
->section
= PROFILE_Load( file
);
668 if(!stat(CurProfile
->unix_name
,&buf
))
669 CurProfile
->mtime
=buf
.st_mtime
;
673 /* Does not exist yet, we will create it in PROFILE_FlushFile */
674 WARN("profile file %s not found\n", debugstr_w(newdos_name
) );
680 /***********************************************************************
683 * Returns all keys of a section.
684 * If return_values is TRUE, also include the corresponding values.
686 static INT
PROFILE_GetSection( PROFILESECTION
*section
, LPCWSTR section_name
,
687 LPWSTR buffer
, UINT len
, BOOL handle_env
,
692 if(!buffer
) return 0;
694 TRACE("%s,%p,%u\n", debugstr_w(section_name
), buffer
, len
);
698 if (section
->name
[0] && !strcmpiW( section
->name
, section_name
))
701 for (key
= section
->key
; key
; key
= key
->next
)
704 if (!*key
->name
) continue; /* Skip empty lines */
705 if (IS_ENTRY_COMMENT(key
->name
)) continue; /* Skip comments */
706 PROFILE_CopyEntry( buffer
, key
->name
, len
- 1, handle_env
, 0 );
707 len
-= strlenW(buffer
) + 1;
708 buffer
+= strlenW(buffer
) + 1;
711 if (return_values
&& key
->value
) {
713 PROFILE_CopyEntry ( buffer
,
714 key
->value
, len
- 1, handle_env
, 0 );
715 len
-= strlenW(buffer
) + 1;
716 buffer
+= strlenW(buffer
) + 1;
721 /*If either lpszSection or lpszKey is NULL and the supplied
722 destination buffer is too small to hold all the strings,
723 the last string is truncated and followed by two null characters.
724 In this case, the return value is equal to cchReturnBuffer
732 section
= section
->next
;
734 buffer
[0] = buffer
[1] = '\0';
738 /* See GetPrivateProfileSectionNamesA for documentation */
739 static INT
PROFILE_GetSectionNames( LPWSTR buffer
, UINT len
)
743 PROFILESECTION
*section
;
754 section
= CurProfile
->section
;
755 while ((section
!=NULL
)) {
756 if (section
->name
[0]) {
757 l
= strlenW(section
->name
)+1;
760 strncpyW(buf
, section
->name
, f
-1);
767 strcpyW(buf
, section
->name
);
771 section
= section
->next
;
778 /***********************************************************************
781 * Get a profile string.
783 * Tests with GetPrivateProfileString16, W95a,
784 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
785 * section key_name def_val res buffer
786 * "set1" "1" "x" 43 [data]
787 * "set1" "1 " "x" 43 [data] (!)
788 * "set1" " 1 "' "x" 43 [data] (!)
789 * "set1" "" "x" 1 "x"
790 * "set1" "" "x " 1 "x" (!)
791 * "set1" "" " x " 3 " x" (!)
792 * "set1" NULL "x" 6 "1\02\03\0\0"
793 * "set1" "" "x" 1 "x"
794 * NULL "1" "x" 0 "" (!)
800 static INT
PROFILE_GetString( LPCWSTR section
, LPCWSTR key_name
,
801 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
803 PROFILEKEY
*key
= NULL
;
804 static const WCHAR empty_strW
[] = { 0 };
806 if(!buffer
) return 0;
808 if (!def_val
) def_val
= empty_strW
;
813 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
816 key
= PROFILE_Find( &CurProfile
->section
, section
, key_name
, FALSE
, FALSE
);
817 PROFILE_CopyEntry( buffer
, (key
&& key
->value
) ? key
->value
: def_val
,
819 TRACE("(%s,%s,%s): returning %s\n",
820 debugstr_w(section
), debugstr_w(key_name
),
821 debugstr_w(def_val
), debugstr_w(buffer
) );
822 return strlenW( buffer
);
824 /* no "else" here ! */
825 if (section
&& section
[0])
827 INT ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
, FALSE
, FALSE
);
828 if (!buffer
[0]) /* no luck -> def_val */
830 PROFILE_CopyEntry(buffer
, def_val
, len
, FALSE
, TRUE
);
831 ret
= strlenW(buffer
);
840 /***********************************************************************
843 * Set a profile string.
845 static BOOL
PROFILE_SetString( LPCWSTR section_name
, LPCWSTR key_name
,
846 LPCWSTR value
, BOOL create_always
)
848 if (!key_name
) /* Delete a whole section */
850 TRACE("(%s)\n", debugstr_w(section_name
));
851 CurProfile
->changed
|= PROFILE_DeleteSection( &CurProfile
->section
,
853 return TRUE
; /* Even if PROFILE_DeleteSection() has failed,
854 this is not an error on application's level.*/
856 else if (!value
) /* Delete a key */
858 TRACE("(%s,%s)\n", debugstr_w(section_name
), debugstr_w(key_name
) );
859 CurProfile
->changed
|= PROFILE_DeleteKey( &CurProfile
->section
,
860 section_name
, key_name
);
861 return TRUE
; /* same error handling as above */
863 else /* Set the key value */
865 PROFILEKEY
*key
= PROFILE_Find(&CurProfile
->section
, section_name
,
866 key_name
, TRUE
, create_always
);
867 TRACE("(%s,%s,%s):\n",
868 debugstr_w(section_name
), debugstr_w(key_name
), debugstr_w(value
) );
869 if (!key
) return FALSE
;
872 /* strip the leading spaces. We can safely strip \n\r and
873 * friends too, they should not happen here anyway. */
874 while (PROFILE_isspace(*value
)) value
++;
876 if (!strcmpW( key
->value
, value
))
878 TRACE(" no change needed\n" );
879 return TRUE
; /* No change needed */
881 TRACE(" replacing %s\n", debugstr_w(key
->value
) );
882 HeapFree( GetProcessHeap(), 0, key
->value
);
884 else TRACE(" creating key\n" );
885 key
->value
= HeapAlloc( GetProcessHeap(), 0, (strlenW(value
)+1) * sizeof(WCHAR
) );
886 strcpyW( key
->value
, value
);
887 CurProfile
->changed
= TRUE
;
893 /***********************************************************************
896 static HKEY
get_profile_key(void)
898 static HKEY profile_key
;
902 OBJECT_ATTRIBUTES attr
;
903 UNICODE_STRING nameW
;
906 attr
.Length
= sizeof(attr
);
907 attr
.RootDirectory
= 0;
908 attr
.ObjectName
= &nameW
;
910 attr
.SecurityDescriptor
= NULL
;
911 attr
.SecurityQualityOfService
= NULL
;
913 if (!RtlCreateUnicodeStringFromAsciiz( &nameW
, "Machine\\Software\\Wine\\Wine\\Config" ) ||
914 NtCreateKey( &hkey
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, REG_OPTION_VOLATILE
, NULL
))
916 ERR("Cannot create config registry key\n" );
919 RtlFreeUnicodeString( &nameW
);
921 if (InterlockedCompareExchangePointer( (void **)&profile_key
, hkey
, 0 ))
922 NtClose( hkey
); /* somebody beat us to it */
928 /***********************************************************************
929 * PROFILE_GetWineIniString
931 * Get a config string from the wine.ini file.
933 int PROFILE_GetWineIniString( LPCWSTR section
, LPCWSTR key_name
,
934 LPCWSTR def
, LPWSTR buffer
, int len
)
938 OBJECT_ATTRIBUTES attr
;
939 UNICODE_STRING nameW
;
941 attr
.Length
= sizeof(attr
);
942 attr
.RootDirectory
= get_profile_key();
943 attr
.ObjectName
= &nameW
;
945 attr
.SecurityDescriptor
= NULL
;
946 attr
.SecurityQualityOfService
= NULL
;
947 RtlInitUnicodeString( &nameW
, section
);
948 if (!(err
= NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
)))
950 char tmp
[PROFILE_MAX_LINE_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
953 RtlInitUnicodeString( &nameW
, key_name
);
954 if (!(err
= NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
955 tmp
, sizeof(tmp
), &count
)))
957 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
958 PROFILE_CopyEntry( buffer
, str
, len
, TRUE
, TRUE
);
963 if (err
) PROFILE_CopyEntry( buffer
, def
, len
, TRUE
, TRUE
);
964 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section
),
965 debugstr_w(key_name
), debugstr_w(def
), debugstr_w(buffer
) );
966 return strlenW(buffer
);
970 /******************************************************************************
972 * PROFILE_GetWineIniBool
974 * Reads a boolean value from the wine.ini file. This function attempts to
975 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
976 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
977 * true. Anything else results in the return of the default value.
979 * This function uses 1 to indicate true, and 0 for false. You can check
980 * for existence by setting def to something other than 0 or 1 and
981 * examining the return value.
983 int PROFILE_GetWineIniBool( LPCWSTR section
, LPCWSTR key_name
, int def
)
985 static const WCHAR def_valueW
[] = {'~',0};
989 PROFILE_GetWineIniString(section
, key_name
, def_valueW
, key_value
, 2);
991 switch(key_value
[0]) {
1012 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section
), debugstr_w(key_name
),
1013 def
? "TRUE" : "FALSE", key_value
[0],
1014 retval
? "TRUE" : "FALSE");
1020 /***********************************************************************
1021 * PROFILE_UsageWineIni
1023 * Explain the wine.ini file to those who don't read documentation.
1024 * Keep below one screenful in length so that error messages above are
1027 void PROFILE_UsageWineIni(void)
1029 MESSAGE("Perhaps you have not properly edited or created "
1030 "your Wine configuration file.\n");
1031 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1032 /* RTFM, so to say */
1036 /********************* API functions **********************************/
1038 /***********************************************************************
1039 * GetProfileInt (KERNEL.57)
1041 UINT16 WINAPI
GetProfileInt16( LPCSTR section
, LPCSTR entry
, INT16 def_val
)
1043 return GetPrivateProfileInt16( section
, entry
, def_val
, "win.ini" );
1047 /***********************************************************************
1048 * GetProfileIntA (KERNEL32.@)
1050 UINT WINAPI
GetProfileIntA( LPCSTR section
, LPCSTR entry
, INT def_val
)
1052 return GetPrivateProfileIntA( section
, entry
, def_val
, "win.ini" );
1055 /***********************************************************************
1056 * GetProfileIntW (KERNEL32.@)
1058 UINT WINAPI
GetProfileIntW( LPCWSTR section
, LPCWSTR entry
, INT def_val
)
1060 return GetPrivateProfileIntW( section
, entry
, def_val
, wininiW
);
1064 * if allow_section_name_copy is TRUE, allow the copying :
1065 * - of Section names if 'section' is NULL
1066 * - of Keys in a Section if 'entry' is NULL
1067 * (see MSDN doc for GetPrivateProfileString)
1069 static int PROFILE_GetPrivateProfileString( LPCWSTR section
, LPCWSTR entry
,
1070 LPCWSTR def_val
, LPWSTR buffer
,
1071 UINT len
, LPCWSTR filename
,
1072 BOOL allow_section_name_copy
)
1075 LPWSTR pDefVal
= NULL
;
1080 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section
), debugstr_w(entry
),
1081 debugstr_w(def_val
), buffer
, len
, debugstr_w(filename
));
1083 /* strip any trailing ' ' of def_val. */
1086 LPCWSTR p
= &def_val
[strlenW(def_val
)]; /* even "" works ! */
1094 if (*p
== ' ') /* ouch, contained trailing ' ' */
1096 int len
= (int)(p
- def_val
);
1097 pDefVal
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1098 strncpyW(pDefVal
, def_val
, len
);
1099 pDefVal
[len
] = '\0';
1103 pDefVal
= (LPWSTR
)def_val
;
1105 EnterCriticalSection( &PROFILE_CritSect
);
1107 if (PROFILE_Open( filename
)) {
1108 if ((allow_section_name_copy
) && (section
== NULL
))
1109 ret
= PROFILE_GetSectionNames(buffer
, len
);
1111 /* PROFILE_GetString already handles the 'entry == NULL' case */
1112 ret
= PROFILE_GetString( section
, entry
, pDefVal
, buffer
, len
);
1114 lstrcpynW( buffer
, pDefVal
, len
);
1115 ret
= strlenW( buffer
);
1118 LeaveCriticalSection( &PROFILE_CritSect
);
1120 if (pDefVal
!= def_val
) /* allocated */
1121 HeapFree(GetProcessHeap(), 0, pDefVal
);
1123 TRACE("returning %s, %d\n", debugstr_w(buffer
), ret
);
1128 /***********************************************************************
1129 * GetPrivateProfileString (KERNEL.128)
1131 INT16 WINAPI
GetPrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1132 LPCSTR def_val
, LPSTR buffer
,
1133 UINT16 len
, LPCSTR filename
)
1135 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1137 INT16 retW
, ret
= 0;
1139 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1140 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1141 else sectionW
.Buffer
= NULL
;
1142 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1143 else entryW
.Buffer
= NULL
;
1144 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1145 else def_valW
.Buffer
= NULL
;
1146 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1147 else filenameW
.Buffer
= NULL
;
1149 retW
= PROFILE_GetPrivateProfileString( sectionW
.Buffer
, entryW
.Buffer
,
1150 def_valW
.Buffer
, bufferW
, len
,
1151 filenameW
.Buffer
, FALSE
);
1154 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1161 ret
--; /* strip terminating 0 */
1164 RtlFreeUnicodeString(§ionW
);
1165 RtlFreeUnicodeString(&entryW
);
1166 RtlFreeUnicodeString(&def_valW
);
1167 RtlFreeUnicodeString(&filenameW
);
1168 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1172 /***********************************************************************
1173 * GetPrivateProfileStringA (KERNEL32.@)
1175 INT WINAPI
GetPrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1176 LPCSTR def_val
, LPSTR buffer
,
1177 UINT len
, LPCSTR filename
)
1179 UNICODE_STRING sectionW
, entryW
, def_valW
, filenameW
;
1183 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1184 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1185 else sectionW
.Buffer
= NULL
;
1186 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1187 else entryW
.Buffer
= NULL
;
1188 if (def_val
) RtlCreateUnicodeStringFromAsciiz(&def_valW
, def_val
);
1189 else def_valW
.Buffer
= NULL
;
1190 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1191 else filenameW
.Buffer
= NULL
;
1193 retW
= GetPrivateProfileStringW( sectionW
.Buffer
, entryW
.Buffer
,
1194 def_valW
.Buffer
, bufferW
, len
,
1198 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 1, buffer
, len
, NULL
, NULL
);
1205 ret
--; /* strip terminating 0 */
1208 RtlFreeUnicodeString(§ionW
);
1209 RtlFreeUnicodeString(&entryW
);
1210 RtlFreeUnicodeString(&def_valW
);
1211 RtlFreeUnicodeString(&filenameW
);
1212 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1216 /***********************************************************************
1217 * GetPrivateProfileStringW (KERNEL32.@)
1219 INT WINAPI
GetPrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1220 LPCWSTR def_val
, LPWSTR buffer
,
1221 UINT len
, LPCWSTR filename
)
1223 return PROFILE_GetPrivateProfileString( section
, entry
, def_val
,
1224 buffer
, len
, filename
, TRUE
);
1227 /***********************************************************************
1228 * GetProfileString (KERNEL.58)
1230 INT16 WINAPI
GetProfileString16( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1231 LPSTR buffer
, UINT16 len
)
1233 return GetPrivateProfileString16( section
, entry
, def_val
,
1234 buffer
, len
, "win.ini" );
1237 /***********************************************************************
1238 * GetProfileStringA (KERNEL32.@)
1240 INT WINAPI
GetProfileStringA( LPCSTR section
, LPCSTR entry
, LPCSTR def_val
,
1241 LPSTR buffer
, UINT len
)
1243 return GetPrivateProfileStringA( section
, entry
, def_val
,
1244 buffer
, len
, "win.ini" );
1247 /***********************************************************************
1248 * GetProfileStringW (KERNEL32.@)
1250 INT WINAPI
GetProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1251 LPCWSTR def_val
, LPWSTR buffer
, UINT len
)
1253 return GetPrivateProfileStringW( section
, entry
, def_val
,
1254 buffer
, len
, wininiW
);
1257 /***********************************************************************
1258 * WriteProfileString (KERNEL.59)
1260 BOOL16 WINAPI
WriteProfileString16( LPCSTR section
, LPCSTR entry
,
1263 return WritePrivateProfileString16( section
, entry
, string
, "win.ini" );
1266 /***********************************************************************
1267 * WriteProfileStringA (KERNEL32.@)
1269 BOOL WINAPI
WriteProfileStringA( LPCSTR section
, LPCSTR entry
,
1272 return WritePrivateProfileStringA( section
, entry
, string
, "win.ini" );
1275 /***********************************************************************
1276 * WriteProfileStringW (KERNEL32.@)
1278 BOOL WINAPI
WriteProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1281 return WritePrivateProfileStringW( section
, entry
, string
, wininiW
);
1285 /***********************************************************************
1286 * GetPrivateProfileInt (KERNEL.127)
1288 UINT16 WINAPI
GetPrivateProfileInt16( LPCSTR section
, LPCSTR entry
,
1289 INT16 def_val
, LPCSTR filename
)
1291 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1292 * here, but Win98SE doesn't care about this at all, so I deleted it.
1293 * AFAIR versions prior to Win9x had these limits, though. */
1294 return (INT16
)GetPrivateProfileIntA(section
,entry
,def_val
,filename
);
1297 /***********************************************************************
1298 * GetPrivateProfileIntA (KERNEL32.@)
1300 UINT WINAPI
GetPrivateProfileIntA( LPCSTR section
, LPCSTR entry
,
1301 INT def_val
, LPCSTR filename
)
1306 if (!GetPrivateProfileStringA( section
, entry
, "",
1307 buffer
, sizeof(buffer
), filename
))
1309 /* FIXME: if entry can be found but it's empty, then Win16 is
1310 * supposed to return 0 instead of def_val ! Difficult/problematic
1311 * to implement (every other failure also returns zero buffer),
1312 * thus wait until testing framework avail for making sure nothing
1313 * else gets broken that way. */
1314 if (!buffer
[0]) return (UINT
)def_val
;
1316 /* Don't use strtol() here !
1317 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1318 YES, scan for unsigned format ! (otherwise compatibility error) */
1319 if (!sscanf(buffer
, "%lu", &result
)) return 0;
1320 return (UINT
)result
;
1323 /***********************************************************************
1324 * GetPrivateProfileIntW (KERNEL32.@)
1326 * FIXME: rewrite using unicode
1328 UINT WINAPI
GetPrivateProfileIntW( LPCWSTR section
, LPCWSTR entry
,
1329 INT def_val
, LPCWSTR filename
)
1331 LPSTR sectionA
= HEAP_strdupWtoA( GetProcessHeap(), 0, section
);
1332 LPSTR entryA
= HEAP_strdupWtoA( GetProcessHeap(), 0, entry
);
1333 LPSTR filenameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
1334 UINT res
= GetPrivateProfileIntA(sectionA
, entryA
, def_val
, filenameA
);
1335 HeapFree( GetProcessHeap(), 0, sectionA
);
1336 HeapFree( GetProcessHeap(), 0, filenameA
);
1337 HeapFree( GetProcessHeap(), 0, entryA
);
1341 /***********************************************************************
1342 * GetPrivateProfileSection (KERNEL.418)
1344 INT16 WINAPI
GetPrivateProfileSection16( LPCSTR section
, LPSTR buffer
,
1345 UINT16 len
, LPCSTR filename
)
1347 return GetPrivateProfileSectionA( section
, buffer
, len
, filename
);
1350 /***********************************************************************
1351 * GetPrivateProfileSectionW (KERNEL32.@)
1353 INT WINAPI
GetPrivateProfileSectionW( LPCWSTR section
, LPWSTR buffer
,
1354 DWORD len
, LPCWSTR filename
)
1358 EnterCriticalSection( &PROFILE_CritSect
);
1360 if (PROFILE_Open( filename
))
1361 ret
= PROFILE_GetSection(CurProfile
->section
, section
, buffer
, len
,
1364 LeaveCriticalSection( &PROFILE_CritSect
);
1369 /***********************************************************************
1370 * GetPrivateProfileSectionA (KERNEL32.@)
1372 INT WINAPI
GetPrivateProfileSectionA( LPCSTR section
, LPSTR buffer
,
1373 DWORD len
, LPCSTR filename
)
1375 UNICODE_STRING sectionW
, filenameW
;
1379 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
)) : NULL
;
1380 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1381 else sectionW
.Buffer
= NULL
;
1382 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1383 else filenameW
.Buffer
= NULL
;
1385 retW
= GetPrivateProfileSectionW(sectionW
.Buffer
, bufferW
, len
, filenameW
.Buffer
);
1388 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
+ 2, buffer
, len
, NULL
, NULL
);
1404 RtlFreeUnicodeString(§ionW
);
1405 RtlFreeUnicodeString(&filenameW
);
1406 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1410 /***********************************************************************
1411 * GetProfileSection (KERNEL.419)
1413 INT16 WINAPI
GetProfileSection16( LPCSTR section
, LPSTR buffer
, UINT16 len
)
1415 return GetPrivateProfileSection16( section
, buffer
, len
, "win.ini" );
1418 /***********************************************************************
1419 * GetProfileSectionA (KERNEL32.@)
1421 INT WINAPI
GetProfileSectionA( LPCSTR section
, LPSTR buffer
, DWORD len
)
1423 return GetPrivateProfileSectionA( section
, buffer
, len
, "win.ini" );
1426 /***********************************************************************
1427 * GetProfileSectionW (KERNEL32.@)
1429 INT WINAPI
GetProfileSectionW( LPCWSTR section
, LPWSTR buffer
, DWORD len
)
1431 return GetPrivateProfileSectionW( section
, buffer
, len
, wininiW
);
1435 /***********************************************************************
1436 * WritePrivateProfileString (KERNEL.129)
1438 BOOL16 WINAPI
WritePrivateProfileString16( LPCSTR section
, LPCSTR entry
,
1439 LPCSTR string
, LPCSTR filename
)
1441 return WritePrivateProfileStringA(section
,entry
,string
,filename
);
1444 /***********************************************************************
1445 * WritePrivateProfileStringW (KERNEL32.@)
1447 BOOL WINAPI
WritePrivateProfileStringW( LPCWSTR section
, LPCWSTR entry
,
1448 LPCWSTR string
, LPCWSTR filename
)
1452 EnterCriticalSection( &PROFILE_CritSect
);
1454 if (PROFILE_Open( filename
))
1456 if (!section
&& !entry
&& !string
) /* documented "file flush" case */
1458 PROFILE_FlushFile();
1459 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1463 FIXME("(NULL?,%s,%s,%s)?\n",
1464 debugstr_w(entry
), debugstr_w(string
), debugstr_w(filename
));
1466 ret
= PROFILE_SetString( section
, entry
, string
, FALSE
);
1467 PROFILE_FlushFile();
1472 LeaveCriticalSection( &PROFILE_CritSect
);
1476 /***********************************************************************
1477 * WritePrivateProfileStringA (KERNEL32.@)
1479 BOOL WINAPI
WritePrivateProfileStringA( LPCSTR section
, LPCSTR entry
,
1480 LPCSTR string
, LPCSTR filename
)
1482 UNICODE_STRING sectionW
, entryW
, stringW
, filenameW
;
1485 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1486 else sectionW
.Buffer
= NULL
;
1487 if (entry
) RtlCreateUnicodeStringFromAsciiz(&entryW
, entry
);
1488 else entryW
.Buffer
= NULL
;
1489 if (string
) RtlCreateUnicodeStringFromAsciiz(&stringW
, string
);
1490 else stringW
.Buffer
= NULL
;
1491 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1492 else filenameW
.Buffer
= NULL
;
1494 ret
= WritePrivateProfileStringW(sectionW
.Buffer
, entryW
.Buffer
,
1495 stringW
.Buffer
, filenameW
.Buffer
);
1496 RtlFreeUnicodeString(§ionW
);
1497 RtlFreeUnicodeString(&entryW
);
1498 RtlFreeUnicodeString(&stringW
);
1499 RtlFreeUnicodeString(&filenameW
);
1503 /***********************************************************************
1504 * WritePrivateProfileSection (KERNEL.416)
1506 BOOL16 WINAPI
WritePrivateProfileSection16( LPCSTR section
,
1507 LPCSTR string
, LPCSTR filename
)
1509 return WritePrivateProfileSectionA( section
, string
, filename
);
1512 /***********************************************************************
1513 * WritePrivateProfileSectionW (KERNEL32.@)
1515 BOOL WINAPI
WritePrivateProfileSectionW( LPCWSTR section
,
1516 LPCWSTR string
, LPCWSTR filename
)
1521 EnterCriticalSection( &PROFILE_CritSect
);
1523 if (PROFILE_Open( filename
)) {
1524 if (!section
&& !string
)
1525 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1526 else if (!string
) {/* delete the named section*/
1527 ret
= PROFILE_SetString(section
,NULL
,NULL
, FALSE
);
1528 PROFILE_FlushFile();
1530 PROFILE_DeleteAllKeys(section
);
1533 LPWSTR buf
= HeapAlloc( GetProcessHeap(), 0, (strlenW(string
)+1) * sizeof(WCHAR
) );
1534 strcpyW( buf
, string
);
1535 if((p
= strchrW( buf
, '='))) {
1537 ret
= PROFILE_SetString( section
, buf
, p
+1, TRUE
);
1539 HeapFree( GetProcessHeap(), 0, buf
);
1540 string
+= strlenW(string
)+1;
1542 PROFILE_FlushFile();
1546 LeaveCriticalSection( &PROFILE_CritSect
);
1550 /***********************************************************************
1551 * WritePrivateProfileSectionA (KERNEL32.@)
1553 BOOL WINAPI
WritePrivateProfileSectionA( LPCSTR section
,
1554 LPCSTR string
, LPCSTR filename
)
1557 UNICODE_STRING sectionW
, filenameW
;
1566 while(*p
) p
+= strlen(p
) + 1;
1567 lenA
= p
- string
+ 1;
1568 lenW
= MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, NULL
, 0);
1569 if ((stringW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
))))
1570 MultiByteToWideChar(CP_ACP
, 0, string
, lenA
, stringW
, lenW
);
1572 else stringW
= NULL
;
1573 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1574 else sectionW
.Buffer
= NULL
;
1575 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1576 else filenameW
.Buffer
= NULL
;
1578 ret
= WritePrivateProfileSectionW(sectionW
.Buffer
, stringW
, filenameW
.Buffer
);
1580 HeapFree(GetProcessHeap(), 0, stringW
);
1581 RtlFreeUnicodeString(§ionW
);
1582 RtlFreeUnicodeString(&filenameW
);
1586 /***********************************************************************
1587 * WriteProfileSection (KERNEL.417)
1589 BOOL16 WINAPI
WriteProfileSection16( LPCSTR section
, LPCSTR keys_n_values
)
1591 return WritePrivateProfileSection16( section
, keys_n_values
, "win.ini");
1594 /***********************************************************************
1595 * WriteProfileSectionA (KERNEL32.@)
1597 BOOL WINAPI
WriteProfileSectionA( LPCSTR section
, LPCSTR keys_n_values
)
1600 return WritePrivateProfileSectionA( section
, keys_n_values
, "win.ini");
1603 /***********************************************************************
1604 * WriteProfileSectionW (KERNEL32.@)
1606 BOOL WINAPI
WriteProfileSectionW( LPCWSTR section
, LPCWSTR keys_n_values
)
1608 return WritePrivateProfileSectionW(section
, keys_n_values
, wininiW
);
1611 /***********************************************************************
1612 * GetPrivateProfileSectionNames (KERNEL.143)
1614 WORD WINAPI
GetPrivateProfileSectionNames16( LPSTR buffer
, WORD size
,
1617 return GetPrivateProfileSectionNamesA(buffer
,size
,filename
);
1621 /***********************************************************************
1622 * GetProfileSectionNames (KERNEL.142)
1624 WORD WINAPI
GetProfileSectionNames16(LPSTR buffer
, WORD size
)
1627 return GetPrivateProfileSectionNamesA(buffer
,size
,"win.ini");
1631 /***********************************************************************
1632 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1634 * Returns the section names contained in the specified file.
1635 * FIXME: Where do we find this file when the path is relative?
1636 * The section names are returned as a list of strings with an extra
1637 * '\0' to mark the end of the list. Except for that the behavior
1638 * depends on the Windows version.
1641 * - if the buffer is 0 or 1 character long then it is as if it was of
1643 * - otherwise, if the buffer is to small only the section names that fit
1645 * - note that this means if the buffer was to small to return even just
1646 * the first section name then a single '\0' will be returned.
1647 * - the return value is the number of characters written in the buffer,
1648 * except if the buffer was too smal in which case len-2 is returned
1651 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1652 * '\0' and the return value is 0
1653 * - otherwise if the buffer is too small then the first section name that
1654 * does not fit is truncated so that the string list can be terminated
1655 * correctly (double '\0')
1656 * - the return value is the number of characters written in the buffer
1657 * except for the trailing '\0'. If the buffer is too small, then the
1658 * return value is len-2
1659 * - Win2000 has a bug that triggers when the section names and the
1660 * trailing '\0' fit exactly in the buffer. In that case the trailing
1663 * Wine implements the observed Win2000 behavior (except for the bug).
1665 * Note that when the buffer is big enough then the return value may be any
1666 * value between 1 and len-1 (or len in Win95), including len-2.
1668 DWORD WINAPI
GetPrivateProfileSectionNamesW( LPWSTR buffer
, DWORD size
,
1673 EnterCriticalSection( &PROFILE_CritSect
);
1675 if (PROFILE_Open( filename
))
1676 ret
= PROFILE_GetSectionNames(buffer
, size
);
1678 LeaveCriticalSection( &PROFILE_CritSect
);
1684 /***********************************************************************
1685 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1687 DWORD WINAPI
GetPrivateProfileSectionNamesA( LPSTR buffer
, DWORD size
,
1690 UNICODE_STRING filenameW
;
1694 bufferW
= buffer
? HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
)) : NULL
;
1695 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1696 else filenameW
.Buffer
= NULL
;
1698 retW
= GetPrivateProfileSectionNamesW(bufferW
, size
, filenameW
.Buffer
);
1701 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, retW
, buffer
, size
, NULL
, NULL
);
1709 RtlFreeUnicodeString(&filenameW
);
1710 if (bufferW
) HeapFree(GetProcessHeap(), 0, bufferW
);
1714 /***********************************************************************
1715 * GetPrivateProfileStruct (KERNEL.407)
1717 BOOL16 WINAPI
GetPrivateProfileStruct16(LPCSTR section
, LPCSTR key
,
1718 LPVOID buf
, UINT16 len
, LPCSTR filename
)
1720 return GetPrivateProfileStructA( section
, key
, buf
, len
, filename
);
1723 /***********************************************************************
1724 * GetPrivateProfileStructW (KERNEL32.@)
1726 * Should match Win95's behaviour pretty much
1728 BOOL WINAPI
GetPrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1729 LPVOID buf
, UINT len
, LPCWSTR filename
)
1733 EnterCriticalSection( &PROFILE_CritSect
);
1735 if (PROFILE_Open( filename
)) {
1736 PROFILEKEY
*k
= PROFILE_Find ( &CurProfile
->section
, section
, key
, FALSE
, FALSE
);
1738 TRACE("value (at %p): %s\n", k
->value
, debugstr_w(k
->value
));
1739 if (((strlenW(k
->value
) - 2) / 2) == len
)
1746 end
= k
->value
+ strlenW(k
->value
); /* -> '\0' */
1747 /* check for invalid chars in ASCII coded hex string */
1748 for (p
=k
->value
; p
< end
; p
++)
1752 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1753 *p
, debugstr_w(filename
), debugstr_w(section
), debugstr_w(key
));
1760 BOOL highnibble
= TRUE
;
1762 LPBYTE binbuf
= (LPBYTE
)buf
;
1764 end
-= 2; /* don't include checksum in output data */
1765 /* translate ASCII hex format into binary data */
1766 for (p
=k
->value
; p
< end
; p
++)
1770 (c
- 'A' + 10) : (c
- '0');
1777 *binbuf
++ = b
; /* feed binary data into output */
1778 chksum
+= b
; /* calculate checksum */
1780 highnibble
^= 1; /* toggle */
1782 /* retrieve stored checksum value */
1784 b
= ( (c
> '9') ? (c
- 'A' + 10) : (c
- '0') ) << 4;
1786 b
+= (c
> '9') ? (c
- 'A' + 10) : (c
- '0');
1787 if (b
== (chksum
& 0xff)) /* checksums match ? */
1793 LeaveCriticalSection( &PROFILE_CritSect
);
1798 /***********************************************************************
1799 * GetPrivateProfileStructA (KERNEL32.@)
1801 BOOL WINAPI
GetPrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1802 LPVOID buffer
, UINT len
, LPCSTR filename
)
1804 UNICODE_STRING sectionW
, keyW
, filenameW
;
1807 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1808 else sectionW
.Buffer
= NULL
;
1809 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1810 else keyW
.Buffer
= NULL
;
1811 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1812 else filenameW
.Buffer
= NULL
;
1814 ret
= GetPrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buffer
, len
,
1816 /* Do not translate binary data. */
1818 RtlFreeUnicodeString(§ionW
);
1819 RtlFreeUnicodeString(&keyW
);
1820 RtlFreeUnicodeString(&filenameW
);
1826 /***********************************************************************
1827 * WritePrivateProfileStruct (KERNEL.406)
1829 BOOL16 WINAPI
WritePrivateProfileStruct16 (LPCSTR section
, LPCSTR key
,
1830 LPVOID buf
, UINT16 bufsize
, LPCSTR filename
)
1832 return WritePrivateProfileStructA( section
, key
, buf
, bufsize
, filename
);
1835 /***********************************************************************
1836 * WritePrivateProfileStructW (KERNEL32.@)
1838 BOOL WINAPI
WritePrivateProfileStructW (LPCWSTR section
, LPCWSTR key
,
1839 LPVOID buf
, UINT bufsize
, LPCWSTR filename
)
1843 LPWSTR outstring
, p
;
1846 if (!section
&& !key
&& !buf
) /* flush the cache */
1847 return WritePrivateProfileStringW( NULL
, NULL
, NULL
, filename
);
1849 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1850 outstring
= HeapAlloc( GetProcessHeap(), 0, (bufsize
*2 + 2 + 1) * sizeof(WCHAR
) );
1852 for (binbuf
= (LPBYTE
)buf
; binbuf
< (LPBYTE
)buf
+bufsize
; binbuf
++) {
1853 *p
++ = hex
[*binbuf
>> 4];
1854 *p
++ = hex
[*binbuf
& 0xf];
1857 /* checksum is sum & 0xff */
1858 *p
++ = hex
[(sum
& 0xf0) >> 4];
1859 *p
++ = hex
[sum
& 0xf];
1862 EnterCriticalSection( &PROFILE_CritSect
);
1864 if (PROFILE_Open( filename
)) {
1865 ret
= PROFILE_SetString( section
, key
, outstring
, FALSE
);
1866 PROFILE_FlushFile();
1869 LeaveCriticalSection( &PROFILE_CritSect
);
1871 HeapFree( GetProcessHeap(), 0, outstring
);
1876 /***********************************************************************
1877 * WritePrivateProfileStructA (KERNEL32.@)
1879 BOOL WINAPI
WritePrivateProfileStructA (LPCSTR section
, LPCSTR key
,
1880 LPVOID buf
, UINT bufsize
, LPCSTR filename
)
1882 UNICODE_STRING sectionW
, keyW
, filenameW
;
1885 if (section
) RtlCreateUnicodeStringFromAsciiz(§ionW
, section
);
1886 else sectionW
.Buffer
= NULL
;
1887 if (key
) RtlCreateUnicodeStringFromAsciiz(&keyW
, key
);
1888 else keyW
.Buffer
= NULL
;
1889 if (filename
) RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
);
1890 else filenameW
.Buffer
= NULL
;
1892 /* Do not translate binary data. */
1893 ret
= WritePrivateProfileStructW(sectionW
.Buffer
, keyW
.Buffer
, buf
, bufsize
,
1896 RtlFreeUnicodeString(§ionW
);
1897 RtlFreeUnicodeString(&keyW
);
1898 RtlFreeUnicodeString(&filenameW
);
1903 /***********************************************************************
1904 * WriteOutProfiles (KERNEL.315)
1906 void WINAPI
WriteOutProfiles16(void)
1908 EnterCriticalSection( &PROFILE_CritSect
);
1909 PROFILE_FlushFile();
1910 LeaveCriticalSection( &PROFILE_CritSect
);
1913 /***********************************************************************
1914 * CloseProfileUserMapping (KERNEL32.@)
1916 BOOL WINAPI
CloseProfileUserMapping(void) {
1917 FIXME("(), stub!\n");
1918 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);