The default behavior of "make everything" is now to build separate
[wine.git] / files / profile.c
blob525e2359a0f08c237c24c947fcd8b4cd64c7eb28
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winerror.h"
41 #include "winternl.h"
42 #include "wine/winbase16.h"
43 #include "drive.h"
44 #include "file.h"
45 #include "heap.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
55 WCHAR *value;
56 struct tagPROFILEKEY *next;
57 WCHAR name[1];
58 } PROFILEKEY;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY *key;
63 struct tagPROFILESECTION *next;
64 WCHAR name[1];
65 } PROFILESECTION;
68 typedef struct
70 BOOL changed;
71 PROFILESECTION *section;
72 WCHAR *dos_name;
73 char *unix_name;
74 WCHAR *filename;
75 time_t mtime;
76 } PROFILE;
79 #define N_CACHED_PROFILES 10
81 /* Cached profile files */
82 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
84 #define CurProfile (MRUProfile[0])
86 /* wine.ini config file registry root */
87 static HKEY wine_profile_key;
89 #define PROFILE_MAX_LINE_LEN 1024
91 /* Wine profile: the profile file being used */
92 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
94 /* Check for comments in profile */
95 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
97 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
99 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
101 static const char hex[16] = "0123456789ABCDEF";
103 /***********************************************************************
104 * PROFILE_CopyEntry
106 * Copy the content of an entry into a buffer, removing quotes, and possibly
107 * translating environment variables.
109 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
110 int handle_env, BOOL strip_quote )
112 WCHAR quote = '\0';
113 LPCWSTR p;
115 if(!buffer) return;
117 if (strip_quote && ((*value == '\'') || (*value == '\"')))
119 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
122 if (!handle_env)
124 lstrcpynW( buffer, value, len );
125 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
126 return;
129 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
131 if ((*p == '$') && (p[1] == '{'))
133 WCHAR env_val[1024];
134 LPCWSTR p2 = strchrW( p, '}' );
135 int copy_len;
136 if (!p2) continue; /* ignore it */
137 copy_len = min( 1024, (int)(p2-p)-1 );
138 strncpyW(env_val, p + 2, copy_len );
139 env_val[copy_len - 1] = 0; /* ensure 0 termination */
140 *buffer = 0;
141 if (GetEnvironmentVariableW( env_val, buffer, len))
143 copy_len = strlenW( buffer );
144 buffer += copy_len;
145 len -= copy_len;
147 p = p2 + 1;
150 if (quote && (len > 1)) buffer--;
151 *buffer = '\0';
155 /***********************************************************************
156 * PROFILE_Save
158 * Save a profile tree to a file.
160 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
162 PROFILEKEY *key;
163 char buffer[PROFILE_MAX_LINE_LEN];
165 for ( ; section; section = section->next)
167 if (section->name[0])
169 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
170 fprintf( file, "\r\n[%s]\r\n", buffer );
172 for (key = section->key; key; key = key->next)
174 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
175 fprintf( file, "%s", buffer );
176 if (key->value)
178 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
179 fprintf( file, "=%s", buffer );
181 fprintf( file, "\r\n" );
187 /***********************************************************************
188 * PROFILE_Free
190 * Free a profile tree.
192 static void PROFILE_Free( PROFILESECTION *section )
194 PROFILESECTION *next_section;
195 PROFILEKEY *key, *next_key;
197 for ( ; section; section = next_section)
199 for (key = section->key; key; key = next_key)
201 next_key = key->next;
202 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
203 HeapFree( GetProcessHeap(), 0, key );
205 next_section = section->next;
206 HeapFree( GetProcessHeap(), 0, section );
210 static inline int PROFILE_isspace(char c)
212 if (isspace(c)) return 1;
213 if (c=='\r' || c==0x1a) return 1;
214 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
215 return 0;
219 /***********************************************************************
220 * PROFILE_Load
222 * Load a profile tree from a file.
224 static PROFILESECTION *PROFILE_Load( FILE *file )
226 char buffer[PROFILE_MAX_LINE_LEN];
227 char *p, *p2;
228 int line = 0, len;
229 PROFILESECTION *section, *first_section;
230 PROFILESECTION **next_section;
231 PROFILEKEY *key, *prev_key, **next_key;
233 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
234 if(first_section == NULL) return NULL;
235 first_section->name[0] = 0;
236 first_section->key = NULL;
237 first_section->next = NULL;
238 next_section = &first_section->next;
239 next_key = &first_section->key;
240 prev_key = NULL;
242 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
244 line++;
245 p = buffer;
246 while (*p && PROFILE_isspace(*p)) p++;
247 if (*p == '[') /* section start */
249 if (!(p2 = strrchr( p, ']' )))
251 WARN("Invalid section header at line %d: '%s'\n",
252 line, p );
254 else
256 *p2 = '\0';
257 p++;
258 len = strlen(p);
259 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
260 break;
261 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
262 section->key = NULL;
263 section->next = NULL;
264 *next_section = section;
265 next_section = &section->next;
266 next_key = &section->key;
267 prev_key = NULL;
269 TRACE("New section: %s\n", debugstr_w(section->name));
271 continue;
275 p2=p+strlen(p) - 1;
276 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
278 if ((p2 = strchr( p, '=' )) != NULL)
280 char *p3 = p2 - 1;
281 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
282 *p2++ = '\0';
283 while (*p2 && PROFILE_isspace(*p2)) p2++;
286 if(*p || !prev_key || *prev_key->name)
288 len = strlen(p);
289 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
290 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
291 if (p2)
293 len = strlen(p2) + 1;
294 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
295 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
297 else key->value = NULL;
299 key->next = NULL;
300 *next_key = key;
301 next_key = &key->next;
302 prev_key = key;
304 TRACE("New key: name=%s, value=%s\n",
305 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
308 return first_section;
312 /***********************************************************************
313 * PROFILE_DeleteSection
315 * Delete a section from a profile tree.
317 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
319 while (*section)
321 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
323 PROFILESECTION *to_del = *section;
324 *section = to_del->next;
325 to_del->next = NULL;
326 PROFILE_Free( to_del );
327 return TRUE;
329 section = &(*section)->next;
331 return FALSE;
335 /***********************************************************************
336 * PROFILE_DeleteKey
338 * Delete a key from a profile tree.
340 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
341 LPCWSTR section_name, LPCWSTR key_name )
343 while (*section)
345 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
347 PROFILEKEY **key = &(*section)->key;
348 while (*key)
350 if (!strcmpiW( (*key)->name, key_name ))
352 PROFILEKEY *to_del = *key;
353 *key = to_del->next;
354 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
355 HeapFree( GetProcessHeap(), 0, to_del );
356 return TRUE;
358 key = &(*key)->next;
361 section = &(*section)->next;
363 return FALSE;
367 /***********************************************************************
368 * PROFILE_DeleteAllKeys
370 * Delete all keys from a profile tree.
372 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
374 PROFILESECTION **section= &CurProfile->section;
375 while (*section)
377 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
379 PROFILEKEY **key = &(*section)->key;
380 while (*key)
382 PROFILEKEY *to_del = *key;
383 *key = to_del->next;
384 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
385 HeapFree( GetProcessHeap(), 0, to_del );
386 CurProfile->changed =TRUE;
389 section = &(*section)->next;
394 /***********************************************************************
395 * PROFILE_Find
397 * Find a key in a profile tree, optionally creating it.
399 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
400 LPCWSTR key_name, BOOL create, BOOL create_always )
402 LPCWSTR p;
403 int seclen, keylen;
405 while (PROFILE_isspace(*section_name)) section_name++;
406 p = section_name + strlenW(section_name) - 1;
407 while ((p > section_name) && PROFILE_isspace(*p)) p--;
408 seclen = p - section_name + 1;
410 while (PROFILE_isspace(*key_name)) key_name++;
411 p = key_name + strlenW(key_name) - 1;
412 while ((p > key_name) && PROFILE_isspace(*p)) p--;
413 keylen = p - key_name + 1;
415 while (*section)
417 if ( ((*section)->name[0])
418 && (!(strncmpiW( (*section)->name, section_name, seclen )))
419 && (((*section)->name)[seclen] == '\0') )
421 PROFILEKEY **key = &(*section)->key;
423 while (*key)
425 /* If create_always is FALSE then we check if the keyname already exists.
426 * Otherwise we add it regardless of its existence, to allow
427 * keys to be added more then once in some cases.
429 if(!create_always)
431 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
432 && (((*key)->name)[keylen] == '\0') )
433 return *key;
435 key = &(*key)->next;
437 if (!create) return NULL;
438 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
439 return NULL;
440 strcpyW( (*key)->name, key_name );
441 (*key)->value = NULL;
442 (*key)->next = NULL;
443 return *key;
445 section = &(*section)->next;
447 if (!create) return NULL;
448 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
449 if(*section == NULL) return NULL;
450 strcpyW( (*section)->name, section_name );
451 (*section)->next = NULL;
452 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
453 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
455 HeapFree(GetProcessHeap(), 0, *section);
456 return NULL;
458 strcpyW( (*section)->key->name, key_name );
459 (*section)->key->value = NULL;
460 (*section)->key->next = NULL;
461 return (*section)->key;
465 /***********************************************************************
466 * PROFILE_FlushFile
468 * Flush the current profile to disk if changed.
470 static BOOL PROFILE_FlushFile(void)
472 char *p, buffer[MAX_PATHNAME_LEN];
473 const char *unix_name;
474 FILE *file = NULL;
475 struct stat buf;
477 if(!CurProfile)
479 WARN("No current profile!\n");
480 return FALSE;
483 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
484 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
486 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
487 WCHAR *name, *name_lwr;
488 /* Try to create it in $HOME/.wine */
489 /* FIXME: this will need a more general solution */
490 strcpy( buffer, wine_get_config_dir() );
491 p = buffer + strlen(buffer);
492 *p++ = '/';
493 *p = 0; /* make strlen() below happy */
494 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
496 /* create a lower cased version of the name */
497 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
498 strcpyW(name_lwr, name);
499 strlwrW(name_lwr);
500 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
501 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
502 HeapFree(GetProcessHeap(), 0, name_lwr);
504 file = fopen( buffer, "w" );
505 unix_name = buffer;
508 if (!file)
510 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
511 return FALSE;
514 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
515 PROFILE_Save( file, CurProfile->section );
516 fclose( file );
517 CurProfile->changed = FALSE;
518 if(!stat(unix_name,&buf))
519 CurProfile->mtime=buf.st_mtime;
520 return TRUE;
524 /***********************************************************************
525 * PROFILE_ReleaseFile
527 * Flush the current profile to disk and remove it from the cache.
529 static void PROFILE_ReleaseFile(void)
531 PROFILE_FlushFile();
532 PROFILE_Free( CurProfile->section );
533 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
534 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
535 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
536 CurProfile->changed = FALSE;
537 CurProfile->section = NULL;
538 CurProfile->dos_name = NULL;
539 CurProfile->unix_name = NULL;
540 CurProfile->filename = NULL;
541 CurProfile->mtime = 0;
545 /***********************************************************************
546 * PROFILE_Open
548 * Open a profile file, checking the cached file first.
550 static BOOL PROFILE_Open( LPCWSTR filename )
552 DOS_FULL_NAME full_name;
553 char buffer[MAX_PATHNAME_LEN];
554 WCHAR *newdos_name;
555 WCHAR *name, *name_lwr;
556 char *p;
557 FILE *file = NULL;
558 int i,j;
559 struct stat buf;
560 PROFILE *tempProfile;
562 /* First time around */
564 if(!CurProfile)
565 for(i=0;i<N_CACHED_PROFILES;i++)
567 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
568 if(MRUProfile[i] == NULL) break;
569 MRUProfile[i]->changed=FALSE;
570 MRUProfile[i]->section=NULL;
571 MRUProfile[i]->dos_name=NULL;
572 MRUProfile[i]->unix_name=NULL;
573 MRUProfile[i]->filename=NULL;
574 MRUProfile[i]->mtime=0;
577 /* Check for a match */
579 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
580 strchrW( filename, ':' ))
582 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
584 else
586 static const WCHAR bkslashW[] = {'\\',0};
587 WCHAR windirW[MAX_PATH];
589 GetWindowsDirectoryW( windirW, MAX_PATH );
590 strcatW( windirW, bkslashW );
591 strcatW( windirW, filename );
592 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
595 for(i=0;i<N_CACHED_PROFILES;i++)
597 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
598 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
600 if(i)
602 PROFILE_FlushFile();
603 tempProfile=MRUProfile[i];
604 for(j=i;j>0;j--)
605 MRUProfile[j]=MRUProfile[j-1];
606 CurProfile=tempProfile;
608 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
609 TRACE("(%s): already opened (mru=%d)\n",
610 debugstr_w(filename), i );
611 else
612 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
613 debugstr_w(filename), i );
614 return TRUE;
618 /* Flush the old current profile */
619 PROFILE_FlushFile();
621 /* Make the oldest profile the current one only in order to get rid of it */
622 if(i==N_CACHED_PROFILES)
624 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
625 for(i=N_CACHED_PROFILES-1;i>0;i--)
626 MRUProfile[i]=MRUProfile[i-1];
627 CurProfile=tempProfile;
629 if(CurProfile->filename) PROFILE_ReleaseFile();
631 /* OK, now that CurProfile is definitely free we assign it our new file */
632 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
633 strcpyW( newdos_name, full_name.short_name );
634 CurProfile->dos_name = newdos_name;
635 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
636 strcpyW( CurProfile->filename, filename );
638 /* Try to open the profile file, first in $HOME/.wine */
640 /* FIXME: this will need a more general solution */
641 strcpy( buffer, wine_get_config_dir() );
642 p = buffer + strlen(buffer);
643 *p++ = '/';
644 *p = 0; /* make strlen() below happy */
645 name = strrchrW( newdos_name, '\\' ) + 1;
647 /* create a lower cased version of the name */
648 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
649 strcpyW(name_lwr, name);
650 strlwrW(name_lwr);
651 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
652 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
653 HeapFree(GetProcessHeap(), 0, name_lwr);
655 if ((file = fopen( buffer, "r" )))
657 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
658 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
659 strcpy( CurProfile->unix_name, buffer );
661 else
663 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
664 strcpy( CurProfile->unix_name, full_name.long_name );
665 if ((file = fopen( full_name.long_name, "r" )))
666 TRACE("(%s): found it in %s\n",
667 debugstr_w(filename), full_name.long_name );
670 if (file)
672 CurProfile->section = PROFILE_Load( file );
673 fclose( file );
674 if(!stat(CurProfile->unix_name,&buf))
675 CurProfile->mtime=buf.st_mtime;
677 else
679 /* Does not exist yet, we will create it in PROFILE_FlushFile */
680 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
682 return TRUE;
686 /***********************************************************************
687 * PROFILE_GetSection
689 * Returns all keys of a section.
690 * If return_values is TRUE, also include the corresponding values.
692 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
693 LPWSTR buffer, UINT len, BOOL handle_env,
694 BOOL return_values )
696 PROFILEKEY *key;
698 if(!buffer) return 0;
700 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
702 while (section)
704 if (section->name[0] && !strcmpiW( section->name, section_name ))
706 UINT oldlen = len;
707 for (key = section->key; key; key = key->next)
709 if (len <= 2) break;
710 if (!*key->name) continue; /* Skip empty lines */
711 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
712 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
713 len -= strlenW(buffer) + 1;
714 buffer += strlenW(buffer) + 1;
715 if (len < 2)
716 break;
717 if (return_values && key->value) {
718 buffer[-1] = '=';
719 PROFILE_CopyEntry ( buffer,
720 key->value, len - 1, handle_env, 0 );
721 len -= strlenW(buffer) + 1;
722 buffer += strlenW(buffer) + 1;
725 *buffer = '\0';
726 if (len <= 1)
727 /*If either lpszSection or lpszKey is NULL and the supplied
728 destination buffer is too small to hold all the strings,
729 the last string is truncated and followed by two null characters.
730 In this case, the return value is equal to cchReturnBuffer
731 minus two. */
733 buffer[-1] = '\0';
734 return oldlen - 2;
736 return oldlen - len;
738 section = section->next;
740 buffer[0] = buffer[1] = '\0';
741 return 0;
744 /* See GetPrivateProfileSectionNamesA for documentation */
745 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
747 LPWSTR buf;
748 UINT f,l;
749 PROFILESECTION *section;
751 if (!buffer || !len)
752 return 0;
753 if (len==1) {
754 *buffer='\0';
755 return 0;
758 f=len-1;
759 buf=buffer;
760 section = CurProfile->section;
761 while ((section!=NULL)) {
762 if (section->name[0]) {
763 l = strlenW(section->name)+1;
764 if (l > f) {
765 if (f>0) {
766 strncpyW(buf, section->name, f-1);
767 buf += f-1;
768 *buf++='\0';
770 *buf='\0';
771 return len-2;
773 strcpyW(buf, section->name);
774 buf += l;
775 f -= l;
777 section = section->next;
779 *buf='\0';
780 return buf-buffer;
784 /***********************************************************************
785 * PROFILE_GetString
787 * Get a profile string.
789 * Tests with GetPrivateProfileString16, W95a,
790 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
791 * section key_name def_val res buffer
792 * "set1" "1" "x" 43 [data]
793 * "set1" "1 " "x" 43 [data] (!)
794 * "set1" " 1 "' "x" 43 [data] (!)
795 * "set1" "" "x" 1 "x"
796 * "set1" "" "x " 1 "x" (!)
797 * "set1" "" " x " 3 " x" (!)
798 * "set1" NULL "x" 6 "1\02\03\0\0"
799 * "set1" "" "x" 1 "x"
800 * NULL "1" "x" 0 "" (!)
801 * "" "1" "x" 1 "x"
802 * NULL NULL "" 0 ""
806 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
807 LPCWSTR def_val, LPWSTR buffer, UINT len )
809 PROFILEKEY *key = NULL;
810 static const WCHAR empty_strW[] = { 0 };
812 if(!buffer) return 0;
814 if (!def_val) def_val = empty_strW;
815 if (key_name)
817 if (!key_name[0])
819 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
820 return 0;
822 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
823 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
824 len, FALSE, TRUE );
825 TRACE("(%s,%s,%s): returning %s\n",
826 debugstr_w(section), debugstr_w(key_name),
827 debugstr_w(def_val), debugstr_w(buffer) );
828 return strlenW( buffer );
830 /* no "else" here ! */
831 if (section && section[0])
833 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
834 if (!buffer[0]) /* no luck -> def_val */
836 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
837 ret = strlenW(buffer);
839 return ret;
841 buffer[0] = '\0';
842 return 0;
846 /***********************************************************************
847 * PROFILE_SetString
849 * Set a profile string.
851 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
852 LPCWSTR value, BOOL create_always )
854 if (!key_name) /* Delete a whole section */
856 TRACE("(%s)\n", debugstr_w(section_name));
857 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
858 section_name );
859 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
860 this is not an error on application's level.*/
862 else if (!value) /* Delete a key */
864 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
865 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
866 section_name, key_name );
867 return TRUE; /* same error handling as above */
869 else /* Set the key value */
871 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
872 key_name, TRUE, create_always );
873 TRACE("(%s,%s,%s):\n",
874 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
875 if (!key) return FALSE;
876 if (key->value)
878 /* strip the leading spaces. We can safely strip \n\r and
879 * friends too, they should not happen here anyway. */
880 while (PROFILE_isspace(*value)) value++;
882 if (!strcmpW( key->value, value ))
884 TRACE(" no change needed\n" );
885 return TRUE; /* No change needed */
887 TRACE(" replacing %s\n", debugstr_w(key->value) );
888 HeapFree( GetProcessHeap(), 0, key->value );
890 else TRACE(" creating key\n" );
891 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
892 strcpyW( key->value, value );
893 CurProfile->changed = TRUE;
895 return TRUE;
899 /***********************************************************************
900 * PROFILE_GetWineIniString
902 * Get a config string from the wine.ini file.
904 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
905 LPCWSTR def, LPWSTR buffer, int len )
907 HKEY hkey;
908 NTSTATUS err;
909 OBJECT_ATTRIBUTES attr;
910 UNICODE_STRING nameW;
912 attr.Length = sizeof(attr);
913 attr.RootDirectory = wine_profile_key;
914 attr.ObjectName = &nameW;
915 attr.Attributes = 0;
916 attr.SecurityDescriptor = NULL;
917 attr.SecurityQualityOfService = NULL;
918 RtlInitUnicodeString( &nameW, section );
919 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
921 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
922 DWORD count;
924 RtlInitUnicodeString( &nameW, key_name );
925 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
926 tmp, sizeof(tmp), &count )))
928 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
929 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
931 NtClose( hkey );
934 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
935 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
936 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
937 return strlenW(buffer);
941 /******************************************************************************
943 * PROFILE_GetWineIniBool
945 * Reads a boolean value from the wine.ini file. This function attempts to
946 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
947 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
948 * true. Anything else results in the return of the default value.
950 * This function uses 1 to indicate true, and 0 for false. You can check
951 * for existence by setting def to something other than 0 or 1 and
952 * examining the return value.
954 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
956 static const WCHAR def_valueW[] = {'~',0};
957 WCHAR key_value[2];
958 int retval;
960 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
962 switch(key_value[0]) {
963 case 'n':
964 case 'N':
965 case 'f':
966 case 'F':
967 case '0':
968 retval = 0;
969 break;
971 case 'y':
972 case 'Y':
973 case 't':
974 case 'T':
975 case '1':
976 retval = 1;
977 break;
979 default:
980 retval = def;
983 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
984 def ? "TRUE" : "FALSE", key_value[0],
985 retval ? "TRUE" : "FALSE");
987 return retval;
991 /***********************************************************************
992 * PROFILE_LoadWineIni
994 * Load the old .winerc file.
996 int PROFILE_LoadWineIni(void)
998 OBJECT_ATTRIBUTES attr;
999 UNICODE_STRING nameW;
1000 char buffer[MAX_PATHNAME_LEN];
1001 const char *p;
1002 FILE *f;
1003 HKEY hKeySW;
1004 DWORD disp;
1006 attr.Length = sizeof(attr);
1007 attr.RootDirectory = 0;
1008 attr.ObjectName = &nameW;
1009 attr.Attributes = 0;
1010 attr.SecurityDescriptor = NULL;
1011 attr.SecurityQualityOfService = NULL;
1013 /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1014 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine" ) ||
1015 NtCreateKey( &hKeySW, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp ))
1017 ERR("Cannot create config registry key\n" );
1018 ExitProcess( 1 );
1020 RtlFreeUnicodeString( &nameW );
1021 NtClose( hKeySW );
1023 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1024 NtCreateKey( &wine_profile_key, KEY_ALL_ACCESS, &attr, 0,
1025 NULL, REG_OPTION_VOLATILE, &disp ))
1027 ERR("Cannot create config registry key\n" );
1028 ExitProcess( 1 );
1030 RtlFreeUnicodeString( &nameW );
1032 if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
1034 MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
1035 return 0;
1039 /***********************************************************************
1040 * PROFILE_UsageWineIni
1042 * Explain the wine.ini file to those who don't read documentation.
1043 * Keep below one screenful in length so that error messages above are
1044 * noticed.
1046 void PROFILE_UsageWineIni(void)
1048 MESSAGE("Perhaps you have not properly edited or created "
1049 "your Wine configuration file.\n");
1050 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1051 /* RTFM, so to say */
1055 /********************* API functions **********************************/
1057 /***********************************************************************
1058 * GetProfileInt (KERNEL.57)
1060 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1062 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1066 /***********************************************************************
1067 * GetProfileIntA (KERNEL32.@)
1069 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1071 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1074 /***********************************************************************
1075 * GetProfileIntW (KERNEL32.@)
1077 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1079 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1083 * if allow_section_name_copy is TRUE, allow the copying :
1084 * - of Section names if 'section' is NULL
1085 * - of Keys in a Section if 'entry' is NULL
1086 * (see MSDN doc for GetPrivateProfileString)
1088 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1089 LPCWSTR def_val, LPWSTR buffer,
1090 UINT len, LPCWSTR filename,
1091 BOOL allow_section_name_copy )
1093 int ret;
1094 LPWSTR pDefVal = NULL;
1096 if (!filename)
1097 filename = wininiW;
1099 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1100 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1102 /* strip any trailing ' ' of def_val. */
1103 if (def_val)
1105 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1107 while (p > def_val)
1109 p--;
1110 if ((*p) != ' ')
1111 break;
1113 if (*p == ' ') /* ouch, contained trailing ' ' */
1115 int len = (int)(p - def_val);
1116 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1117 strncpyW(pDefVal, def_val, len);
1118 pDefVal[len] = '\0';
1121 if (!pDefVal)
1122 pDefVal = (LPWSTR)def_val;
1124 EnterCriticalSection( &PROFILE_CritSect );
1126 if (PROFILE_Open( filename )) {
1127 if ((allow_section_name_copy) && (section == NULL))
1128 ret = PROFILE_GetSectionNames(buffer, len);
1129 else
1130 /* PROFILE_GetString already handles the 'entry == NULL' case */
1131 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1132 } else {
1133 lstrcpynW( buffer, pDefVal, len );
1134 ret = strlenW( buffer );
1137 LeaveCriticalSection( &PROFILE_CritSect );
1139 if (pDefVal != def_val) /* allocated */
1140 HeapFree(GetProcessHeap(), 0, pDefVal);
1142 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1144 return ret;
1147 /***********************************************************************
1148 * GetPrivateProfileString (KERNEL.128)
1150 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1151 LPCSTR def_val, LPSTR buffer,
1152 UINT16 len, LPCSTR filename )
1154 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1155 LPWSTR bufferW;
1156 INT16 retW, ret = 0;
1158 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1159 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1160 else sectionW.Buffer = NULL;
1161 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1162 else entryW.Buffer = NULL;
1163 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1164 else def_valW.Buffer = NULL;
1165 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1166 else filenameW.Buffer = NULL;
1168 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1169 def_valW.Buffer, bufferW, len,
1170 filenameW.Buffer, FALSE );
1171 if (len)
1173 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1174 if (!ret)
1176 ret = len - 1;
1177 buffer[ret] = 0;
1179 else
1180 ret--; /* strip terminating 0 */
1183 RtlFreeUnicodeString(&sectionW);
1184 RtlFreeUnicodeString(&entryW);
1185 RtlFreeUnicodeString(&def_valW);
1186 RtlFreeUnicodeString(&filenameW);
1187 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1188 return ret;
1191 /***********************************************************************
1192 * GetPrivateProfileStringA (KERNEL32.@)
1194 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1195 LPCSTR def_val, LPSTR buffer,
1196 UINT len, LPCSTR filename )
1198 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1199 LPWSTR bufferW;
1200 INT retW, ret = 0;
1202 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1203 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1204 else sectionW.Buffer = NULL;
1205 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1206 else entryW.Buffer = NULL;
1207 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1208 else def_valW.Buffer = NULL;
1209 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1210 else filenameW.Buffer = NULL;
1212 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1213 def_valW.Buffer, bufferW, len,
1214 filenameW.Buffer);
1215 if (len)
1217 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1218 if (!ret)
1220 ret = len - 1;
1221 buffer[ret] = 0;
1223 else
1224 ret--; /* strip terminating 0 */
1227 RtlFreeUnicodeString(&sectionW);
1228 RtlFreeUnicodeString(&entryW);
1229 RtlFreeUnicodeString(&def_valW);
1230 RtlFreeUnicodeString(&filenameW);
1231 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1232 return ret;
1235 /***********************************************************************
1236 * GetPrivateProfileStringW (KERNEL32.@)
1238 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1239 LPCWSTR def_val, LPWSTR buffer,
1240 UINT len, LPCWSTR filename )
1242 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1243 buffer, len, filename, TRUE );
1246 /***********************************************************************
1247 * GetProfileString (KERNEL.58)
1249 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1250 LPSTR buffer, UINT16 len )
1252 return GetPrivateProfileString16( section, entry, def_val,
1253 buffer, len, "win.ini" );
1256 /***********************************************************************
1257 * GetProfileStringA (KERNEL32.@)
1259 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1260 LPSTR buffer, UINT len )
1262 return GetPrivateProfileStringA( section, entry, def_val,
1263 buffer, len, "win.ini" );
1266 /***********************************************************************
1267 * GetProfileStringW (KERNEL32.@)
1269 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1270 LPCWSTR def_val, LPWSTR buffer, UINT len )
1272 return GetPrivateProfileStringW( section, entry, def_val,
1273 buffer, len, wininiW );
1276 /***********************************************************************
1277 * WriteProfileString (KERNEL.59)
1279 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1280 LPCSTR string )
1282 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1285 /***********************************************************************
1286 * WriteProfileStringA (KERNEL32.@)
1288 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1289 LPCSTR string )
1291 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1294 /***********************************************************************
1295 * WriteProfileStringW (KERNEL32.@)
1297 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1298 LPCWSTR string )
1300 return WritePrivateProfileStringW( section, entry, string, wininiW );
1304 /***********************************************************************
1305 * GetPrivateProfileInt (KERNEL.127)
1307 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1308 INT16 def_val, LPCSTR filename )
1310 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1311 * here, but Win98SE doesn't care about this at all, so I deleted it.
1312 * AFAIR versions prior to Win9x had these limits, though. */
1313 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1316 /***********************************************************************
1317 * GetPrivateProfileIntA (KERNEL32.@)
1319 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1320 INT def_val, LPCSTR filename )
1322 char buffer[20];
1323 long result;
1325 if (!GetPrivateProfileStringA( section, entry, "",
1326 buffer, sizeof(buffer), filename ))
1327 return def_val;
1328 /* FIXME: if entry can be found but it's empty, then Win16 is
1329 * supposed to return 0 instead of def_val ! Difficult/problematic
1330 * to implement (every other failure also returns zero buffer),
1331 * thus wait until testing framework avail for making sure nothing
1332 * else gets broken that way. */
1333 if (!buffer[0]) return (UINT)def_val;
1335 /* Don't use strtol() here !
1336 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1337 YES, scan for unsigned format ! (otherwise compatibility error) */
1338 if (!sscanf(buffer, "%lu", &result)) return 0;
1339 return (UINT)result;
1342 /***********************************************************************
1343 * GetPrivateProfileIntW (KERNEL32.@)
1345 * FIXME: rewrite using unicode
1347 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1348 INT def_val, LPCWSTR filename )
1350 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1351 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1352 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1353 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1354 HeapFree( GetProcessHeap(), 0, sectionA );
1355 HeapFree( GetProcessHeap(), 0, filenameA );
1356 HeapFree( GetProcessHeap(), 0, entryA );
1357 return res;
1360 /***********************************************************************
1361 * GetPrivateProfileSection (KERNEL.418)
1363 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1364 UINT16 len, LPCSTR filename )
1366 return GetPrivateProfileSectionA( section, buffer, len, filename );
1369 /***********************************************************************
1370 * GetPrivateProfileSectionW (KERNEL32.@)
1372 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1373 DWORD len, LPCWSTR filename )
1375 int ret = 0;
1377 EnterCriticalSection( &PROFILE_CritSect );
1379 if (PROFILE_Open( filename ))
1380 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1381 FALSE, TRUE);
1383 LeaveCriticalSection( &PROFILE_CritSect );
1385 return ret;
1388 /***********************************************************************
1389 * GetPrivateProfileSectionA (KERNEL32.@)
1391 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1392 DWORD len, LPCSTR filename )
1394 UNICODE_STRING sectionW, filenameW;
1395 LPWSTR bufferW;
1396 INT retW, ret = 0;
1398 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1399 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1400 else sectionW.Buffer = NULL;
1401 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1402 else filenameW.Buffer = NULL;
1404 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1405 if (len > 2)
1407 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1408 if (ret > 2)
1409 ret -= 2;
1410 else
1412 ret = 0;
1413 buffer[len-2] = 0;
1414 buffer[len-1] = 0;
1417 else
1419 buffer[0] = 0;
1420 buffer[1] = 0;
1423 RtlFreeUnicodeString(&sectionW);
1424 RtlFreeUnicodeString(&filenameW);
1425 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1426 return ret;
1429 /***********************************************************************
1430 * GetProfileSection (KERNEL.419)
1432 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1434 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1437 /***********************************************************************
1438 * GetProfileSectionA (KERNEL32.@)
1440 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1442 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1445 /***********************************************************************
1446 * GetProfileSectionW (KERNEL32.@)
1448 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1450 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1454 /***********************************************************************
1455 * WritePrivateProfileString (KERNEL.129)
1457 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1458 LPCSTR string, LPCSTR filename )
1460 return WritePrivateProfileStringA(section,entry,string,filename);
1463 /***********************************************************************
1464 * WritePrivateProfileStringW (KERNEL32.@)
1466 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1467 LPCWSTR string, LPCWSTR filename )
1469 BOOL ret = FALSE;
1471 EnterCriticalSection( &PROFILE_CritSect );
1473 if (PROFILE_Open( filename ))
1475 if (!section && !entry && !string) /* documented "file flush" case */
1477 PROFILE_FlushFile();
1478 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1480 else {
1481 if (!section) {
1482 FIXME("(NULL?,%s,%s,%s)?\n",
1483 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1484 } else {
1485 ret = PROFILE_SetString( section, entry, string, FALSE);
1486 PROFILE_FlushFile();
1491 LeaveCriticalSection( &PROFILE_CritSect );
1492 return ret;
1495 /***********************************************************************
1496 * WritePrivateProfileStringA (KERNEL32.@)
1498 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1499 LPCSTR string, LPCSTR filename )
1501 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1502 BOOL ret;
1504 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1505 else sectionW.Buffer = NULL;
1506 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1507 else entryW.Buffer = NULL;
1508 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1509 else stringW.Buffer = NULL;
1510 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1511 else filenameW.Buffer = NULL;
1513 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1514 stringW.Buffer, filenameW.Buffer);
1515 RtlFreeUnicodeString(&sectionW);
1516 RtlFreeUnicodeString(&entryW);
1517 RtlFreeUnicodeString(&stringW);
1518 RtlFreeUnicodeString(&filenameW);
1519 return ret;
1522 /***********************************************************************
1523 * WritePrivateProfileSection (KERNEL.416)
1525 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1526 LPCSTR string, LPCSTR filename )
1528 return WritePrivateProfileSectionA( section, string, filename );
1531 /***********************************************************************
1532 * WritePrivateProfileSectionW (KERNEL32.@)
1534 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1535 LPCWSTR string, LPCWSTR filename )
1537 BOOL ret = FALSE;
1538 LPWSTR p;
1540 EnterCriticalSection( &PROFILE_CritSect );
1542 if (PROFILE_Open( filename )) {
1543 if (!section && !string)
1544 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1545 else if (!string) {/* delete the named section*/
1546 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1547 PROFILE_FlushFile();
1548 } else {
1549 PROFILE_DeleteAllKeys(section);
1550 ret = TRUE;
1551 while(*string) {
1552 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1553 strcpyW( buf, string );
1554 if((p = strchrW( buf, '='))) {
1555 *p='\0';
1556 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1558 HeapFree( GetProcessHeap(), 0, buf );
1559 string += strlenW(string)+1;
1561 PROFILE_FlushFile();
1565 LeaveCriticalSection( &PROFILE_CritSect );
1566 return ret;
1569 /***********************************************************************
1570 * WritePrivateProfileSectionA (KERNEL32.@)
1572 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1573 LPCSTR string, LPCSTR filename)
1576 UNICODE_STRING sectionW, filenameW;
1577 LPWSTR stringW;
1578 BOOL ret;
1580 if (string)
1582 INT lenA, lenW;
1583 LPCSTR p = string;
1585 while(*p) p += strlen(p) + 1;
1586 lenA = p - string + 1;
1587 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1588 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1589 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1591 else stringW = NULL;
1592 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1593 else sectionW.Buffer = NULL;
1594 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1595 else filenameW.Buffer = NULL;
1597 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1599 HeapFree(GetProcessHeap(), 0, stringW);
1600 RtlFreeUnicodeString(&sectionW);
1601 RtlFreeUnicodeString(&filenameW);
1602 return ret;
1605 /***********************************************************************
1606 * WriteProfileSection (KERNEL.417)
1608 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1610 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1613 /***********************************************************************
1614 * WriteProfileSectionA (KERNEL32.@)
1616 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1619 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1622 /***********************************************************************
1623 * WriteProfileSectionW (KERNEL32.@)
1625 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1627 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1630 /***********************************************************************
1631 * GetPrivateProfileSectionNames (KERNEL.143)
1633 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1634 LPCSTR filename )
1636 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1640 /***********************************************************************
1641 * GetProfileSectionNames (KERNEL.142)
1643 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1646 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1650 /***********************************************************************
1651 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1653 * Returns the section names contained in the specified file.
1654 * FIXME: Where do we find this file when the path is relative?
1655 * The section names are returned as a list of strings with an extra
1656 * '\0' to mark the end of the list. Except for that the behavior
1657 * depends on the Windows version.
1659 * Win95:
1660 * - if the buffer is 0 or 1 character long then it is as if it was of
1661 * infinite length.
1662 * - otherwise, if the buffer is to small only the section names that fit
1663 * are returned.
1664 * - note that this means if the buffer was to small to return even just
1665 * the first section name then a single '\0' will be returned.
1666 * - the return value is the number of characters written in the buffer,
1667 * except if the buffer was too smal in which case len-2 is returned
1669 * Win2000:
1670 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1671 * '\0' and the return value is 0
1672 * - otherwise if the buffer is too small then the first section name that
1673 * does not fit is truncated so that the string list can be terminated
1674 * correctly (double '\0')
1675 * - the return value is the number of characters written in the buffer
1676 * except for the trailing '\0'. If the buffer is too small, then the
1677 * return value is len-2
1678 * - Win2000 has a bug that triggers when the section names and the
1679 * trailing '\0' fit exactly in the buffer. In that case the trailing
1680 * '\0' is missing.
1682 * Wine implements the observed Win2000 behavior (except for the bug).
1684 * Note that when the buffer is big enough then the return value may be any
1685 * value between 1 and len-1 (or len in Win95), including len-2.
1687 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1688 LPCWSTR filename)
1690 DWORD ret = 0;
1692 EnterCriticalSection( &PROFILE_CritSect );
1694 if (PROFILE_Open( filename ))
1695 ret = PROFILE_GetSectionNames(buffer, size);
1697 LeaveCriticalSection( &PROFILE_CritSect );
1699 return ret;
1703 /***********************************************************************
1704 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1706 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1707 LPCSTR filename)
1709 UNICODE_STRING filenameW;
1710 LPWSTR bufferW;
1711 INT retW, ret = 0;
1713 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1714 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1715 else filenameW.Buffer = NULL;
1717 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1718 if (retW && size)
1720 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1721 if (!ret)
1723 ret = size;
1724 buffer[size-1] = 0;
1728 RtlFreeUnicodeString(&filenameW);
1729 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1730 return ret;
1733 /***********************************************************************
1734 * GetPrivateProfileStruct (KERNEL.407)
1736 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1737 LPVOID buf, UINT16 len, LPCSTR filename)
1739 return GetPrivateProfileStructA( section, key, buf, len, filename );
1742 /***********************************************************************
1743 * GetPrivateProfileStructW (KERNEL32.@)
1745 * Should match Win95's behaviour pretty much
1747 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1748 LPVOID buf, UINT len, LPCWSTR filename)
1750 BOOL ret = FALSE;
1752 EnterCriticalSection( &PROFILE_CritSect );
1754 if (PROFILE_Open( filename )) {
1755 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1756 if (k) {
1757 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1758 if (((strlenW(k->value) - 2) / 2) == len)
1760 LPWSTR end, p;
1761 BOOL valid = TRUE;
1762 WCHAR c;
1763 DWORD chksum = 0;
1765 end = k->value + strlenW(k->value); /* -> '\0' */
1766 /* check for invalid chars in ASCII coded hex string */
1767 for (p=k->value; p < end; p++)
1769 if (!isxdigitW(*p))
1771 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1772 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1773 valid = FALSE;
1774 break;
1777 if (valid)
1779 BOOL highnibble = TRUE;
1780 BYTE b = 0, val;
1781 LPBYTE binbuf = (LPBYTE)buf;
1783 end -= 2; /* don't include checksum in output data */
1784 /* translate ASCII hex format into binary data */
1785 for (p=k->value; p < end; p++)
1787 c = toupperW(*p);
1788 val = (c > '9') ?
1789 (c - 'A' + 10) : (c - '0');
1791 if (highnibble)
1792 b = val << 4;
1793 else
1795 b += val;
1796 *binbuf++ = b; /* feed binary data into output */
1797 chksum += b; /* calculate checksum */
1799 highnibble ^= 1; /* toggle */
1801 /* retrieve stored checksum value */
1802 c = toupperW(*p++);
1803 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1804 c = toupperW(*p);
1805 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1806 if (b == (chksum & 0xff)) /* checksums match ? */
1807 ret = TRUE;
1812 LeaveCriticalSection( &PROFILE_CritSect );
1814 return ret;
1817 /***********************************************************************
1818 * GetPrivateProfileStructA (KERNEL32.@)
1820 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1821 LPVOID buffer, UINT len, LPCSTR filename)
1823 UNICODE_STRING sectionW, keyW, filenameW;
1824 INT ret;
1826 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1827 else sectionW.Buffer = NULL;
1828 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1829 else keyW.Buffer = NULL;
1830 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1831 else filenameW.Buffer = NULL;
1833 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1834 filenameW.Buffer);
1835 /* Do not translate binary data. */
1837 RtlFreeUnicodeString(&sectionW);
1838 RtlFreeUnicodeString(&keyW);
1839 RtlFreeUnicodeString(&filenameW);
1840 return ret;
1845 /***********************************************************************
1846 * WritePrivateProfileStruct (KERNEL.406)
1848 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1849 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1851 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1854 /***********************************************************************
1855 * WritePrivateProfileStructW (KERNEL32.@)
1857 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1858 LPVOID buf, UINT bufsize, LPCWSTR filename)
1860 BOOL ret = FALSE;
1861 LPBYTE binbuf;
1862 LPWSTR outstring, p;
1863 DWORD sum = 0;
1865 if (!section && !key && !buf) /* flush the cache */
1866 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1868 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1869 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1870 p = outstring;
1871 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1872 *p++ = hex[*binbuf >> 4];
1873 *p++ = hex[*binbuf & 0xf];
1874 sum += *binbuf;
1876 /* checksum is sum & 0xff */
1877 *p++ = hex[(sum & 0xf0) >> 4];
1878 *p++ = hex[sum & 0xf];
1879 *p++ = '\0';
1881 EnterCriticalSection( &PROFILE_CritSect );
1883 if (PROFILE_Open( filename )) {
1884 ret = PROFILE_SetString( section, key, outstring, FALSE);
1885 PROFILE_FlushFile();
1888 LeaveCriticalSection( &PROFILE_CritSect );
1890 HeapFree( GetProcessHeap(), 0, outstring );
1892 return ret;
1895 /***********************************************************************
1896 * WritePrivateProfileStructA (KERNEL32.@)
1898 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1899 LPVOID buf, UINT bufsize, LPCSTR filename)
1901 UNICODE_STRING sectionW, keyW, filenameW;
1902 INT ret;
1904 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1905 else sectionW.Buffer = NULL;
1906 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1907 else keyW.Buffer = NULL;
1908 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1909 else filenameW.Buffer = NULL;
1911 /* Do not translate binary data. */
1912 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1913 filenameW.Buffer);
1915 RtlFreeUnicodeString(&sectionW);
1916 RtlFreeUnicodeString(&keyW);
1917 RtlFreeUnicodeString(&filenameW);
1918 return ret;
1922 /***********************************************************************
1923 * WriteOutProfiles (KERNEL.315)
1925 void WINAPI WriteOutProfiles16(void)
1927 EnterCriticalSection( &PROFILE_CritSect );
1928 PROFILE_FlushFile();
1929 LeaveCriticalSection( &PROFILE_CritSect );
1932 /***********************************************************************
1933 * CloseProfileUserMapping (KERNEL32.@)
1935 BOOL WINAPI CloseProfileUserMapping(void) {
1936 FIXME("(), stub!\n");
1937 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1938 return FALSE;