Correctly encode wParam for WM_SYSCOMMAND,SC_SIZE messages.
[wine/multimedia.git] / files / profile.c
blob277e811a3bf9ab4f6f55aa5cebac6dc0c2f579b5
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <ctype.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winnls.h"
39 #include "winerror.h"
40 #include "winternl.h"
41 #include "wine/winbase16.h"
42 #include "drive.h"
43 #include "file.h"
44 #include "heap.h"
45 #include "wine/unicode.h"
46 #include "wine/server.h"
47 #include "wine/library.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(profile);
52 typedef struct tagPROFILEKEY
54 WCHAR *value;
55 struct tagPROFILEKEY *next;
56 WCHAR name[1];
57 } PROFILEKEY;
59 typedef struct tagPROFILESECTION
61 struct tagPROFILEKEY *key;
62 struct tagPROFILESECTION *next;
63 WCHAR name[1];
64 } PROFILESECTION;
67 typedef struct
69 BOOL changed;
70 PROFILESECTION *section;
71 WCHAR *dos_name;
72 char *unix_name;
73 WCHAR *filename;
74 time_t mtime;
75 } PROFILE;
78 #define N_CACHED_PROFILES 10
80 /* Cached profile files */
81 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
83 #define CurProfile (MRUProfile[0])
85 #define PROFILE_MAX_LINE_LEN 1024
87 /* Check for comments in profile */
88 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
90 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
92 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
94 static const char hex[16] = "0123456789ABCDEF";
96 /***********************************************************************
97 * PROFILE_CopyEntry
99 * Copy the content of an entry into a buffer, removing quotes, and possibly
100 * translating environment variables.
102 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
103 int handle_env, BOOL strip_quote )
105 WCHAR quote = '\0';
106 LPCWSTR p;
108 if(!buffer) return;
110 if (strip_quote && ((*value == '\'') || (*value == '\"')))
112 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
115 if (!handle_env)
117 lstrcpynW( buffer, value, len );
118 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
119 return;
122 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
124 if ((*p == '$') && (p[1] == '{'))
126 WCHAR env_val[1024];
127 LPCWSTR p2 = strchrW( p, '}' );
128 int copy_len;
129 if (!p2) continue; /* ignore it */
130 copy_len = min( 1024, (int)(p2-p)-1 );
131 strncpyW(env_val, p + 2, copy_len );
132 env_val[copy_len - 1] = 0; /* ensure 0 termination */
133 *buffer = 0;
134 if (GetEnvironmentVariableW( env_val, buffer, len))
136 copy_len = strlenW( buffer );
137 buffer += copy_len;
138 len -= copy_len;
140 p = p2 + 1;
143 if (quote && (len > 1)) buffer--;
144 *buffer = '\0';
148 /***********************************************************************
149 * PROFILE_Save
151 * Save a profile tree to a file.
153 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
155 PROFILEKEY *key;
156 char buffer[PROFILE_MAX_LINE_LEN];
158 for ( ; section; section = section->next)
160 if (section->name[0])
162 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
163 fprintf( file, "\r\n[%s]\r\n", buffer );
165 for (key = section->key; key; key = key->next)
167 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
168 fprintf( file, "%s", buffer );
169 if (key->value)
171 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
172 fprintf( file, "=%s", buffer );
174 fprintf( file, "\r\n" );
180 /***********************************************************************
181 * PROFILE_Free
183 * Free a profile tree.
185 static void PROFILE_Free( PROFILESECTION *section )
187 PROFILESECTION *next_section;
188 PROFILEKEY *key, *next_key;
190 for ( ; section; section = next_section)
192 for (key = section->key; key; key = next_key)
194 next_key = key->next;
195 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
196 HeapFree( GetProcessHeap(), 0, key );
198 next_section = section->next;
199 HeapFree( GetProcessHeap(), 0, section );
203 static inline int PROFILE_isspace(char c)
205 if (isspace(c)) return 1;
206 if (c=='\r' || c==0x1a) return 1;
207 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
208 return 0;
212 /***********************************************************************
213 * PROFILE_Load
215 * Load a profile tree from a file.
217 static PROFILESECTION *PROFILE_Load( FILE *file )
219 char buffer[PROFILE_MAX_LINE_LEN];
220 char *p, *p2;
221 int line = 0, len;
222 PROFILESECTION *section, *first_section;
223 PROFILESECTION **next_section;
224 PROFILEKEY *key, *prev_key, **next_key;
226 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
227 if(first_section == NULL) return NULL;
228 first_section->name[0] = 0;
229 first_section->key = NULL;
230 first_section->next = NULL;
231 next_section = &first_section->next;
232 next_key = &first_section->key;
233 prev_key = NULL;
235 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
237 line++;
238 p = buffer;
239 while (*p && PROFILE_isspace(*p)) p++;
240 if (*p == '[') /* section start */
242 if (!(p2 = strrchr( p, ']' )))
244 WARN("Invalid section header at line %d: '%s'\n",
245 line, p );
247 else
249 *p2 = '\0';
250 p++;
251 len = strlen(p);
252 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
253 break;
254 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
255 section->key = NULL;
256 section->next = NULL;
257 *next_section = section;
258 next_section = &section->next;
259 next_key = &section->key;
260 prev_key = NULL;
262 TRACE("New section: %s\n", debugstr_w(section->name));
264 continue;
268 p2=p+strlen(p) - 1;
269 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
271 if ((p2 = strchr( p, '=' )) != NULL)
273 char *p3 = p2 - 1;
274 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
275 *p2++ = '\0';
276 while (*p2 && PROFILE_isspace(*p2)) p2++;
279 if(*p || !prev_key || *prev_key->name)
281 len = strlen(p);
282 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
283 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
284 if (p2)
286 len = strlen(p2) + 1;
287 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
288 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
290 else key->value = NULL;
292 key->next = NULL;
293 *next_key = key;
294 next_key = &key->next;
295 prev_key = key;
297 TRACE("New key: name=%s, value=%s\n",
298 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
301 return first_section;
305 /***********************************************************************
306 * PROFILE_DeleteSection
308 * Delete a section from a profile tree.
310 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
312 while (*section)
314 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
316 PROFILESECTION *to_del = *section;
317 *section = to_del->next;
318 to_del->next = NULL;
319 PROFILE_Free( to_del );
320 return TRUE;
322 section = &(*section)->next;
324 return FALSE;
328 /***********************************************************************
329 * PROFILE_DeleteKey
331 * Delete a key from a profile tree.
333 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
334 LPCWSTR section_name, LPCWSTR key_name )
336 while (*section)
338 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
340 PROFILEKEY **key = &(*section)->key;
341 while (*key)
343 if (!strcmpiW( (*key)->name, key_name ))
345 PROFILEKEY *to_del = *key;
346 *key = to_del->next;
347 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
348 HeapFree( GetProcessHeap(), 0, to_del );
349 return TRUE;
351 key = &(*key)->next;
354 section = &(*section)->next;
356 return FALSE;
360 /***********************************************************************
361 * PROFILE_DeleteAllKeys
363 * Delete all keys from a profile tree.
365 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
367 PROFILESECTION **section= &CurProfile->section;
368 while (*section)
370 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
372 PROFILEKEY **key = &(*section)->key;
373 while (*key)
375 PROFILEKEY *to_del = *key;
376 *key = to_del->next;
377 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
378 HeapFree( GetProcessHeap(), 0, to_del );
379 CurProfile->changed =TRUE;
382 section = &(*section)->next;
387 /***********************************************************************
388 * PROFILE_Find
390 * Find a key in a profile tree, optionally creating it.
392 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
393 LPCWSTR key_name, BOOL create, BOOL create_always )
395 LPCWSTR p;
396 int seclen, keylen;
398 while (PROFILE_isspace(*section_name)) section_name++;
399 p = section_name + strlenW(section_name) - 1;
400 while ((p > section_name) && PROFILE_isspace(*p)) p--;
401 seclen = p - section_name + 1;
403 while (PROFILE_isspace(*key_name)) key_name++;
404 p = key_name + strlenW(key_name) - 1;
405 while ((p > key_name) && PROFILE_isspace(*p)) p--;
406 keylen = p - key_name + 1;
408 while (*section)
410 if ( ((*section)->name[0])
411 && (!(strncmpiW( (*section)->name, section_name, seclen )))
412 && (((*section)->name)[seclen] == '\0') )
414 PROFILEKEY **key = &(*section)->key;
416 while (*key)
418 /* If create_always is FALSE then we check if the keyname already exists.
419 * Otherwise we add it regardless of its existence, to allow
420 * keys to be added more then once in some cases.
422 if(!create_always)
424 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
425 && (((*key)->name)[keylen] == '\0') )
426 return *key;
428 key = &(*key)->next;
430 if (!create) return NULL;
431 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
432 return NULL;
433 strcpyW( (*key)->name, key_name );
434 (*key)->value = NULL;
435 (*key)->next = NULL;
436 return *key;
438 section = &(*section)->next;
440 if (!create) return NULL;
441 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
442 if(*section == NULL) return NULL;
443 strcpyW( (*section)->name, section_name );
444 (*section)->next = NULL;
445 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
446 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
448 HeapFree(GetProcessHeap(), 0, *section);
449 return NULL;
451 strcpyW( (*section)->key->name, key_name );
452 (*section)->key->value = NULL;
453 (*section)->key->next = NULL;
454 return (*section)->key;
458 /***********************************************************************
459 * PROFILE_FlushFile
461 * Flush the current profile to disk if changed.
463 static BOOL PROFILE_FlushFile(void)
465 char *p, buffer[MAX_PATHNAME_LEN];
466 const char *unix_name;
467 FILE *file = NULL;
468 struct stat buf;
470 if(!CurProfile)
472 WARN("No current profile!\n");
473 return FALSE;
476 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
477 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
479 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
480 WCHAR *name, *name_lwr;
481 /* Try to create it in $HOME/.wine */
482 /* FIXME: this will need a more general solution */
483 strcpy( buffer, wine_get_config_dir() );
484 p = buffer + strlen(buffer);
485 *p++ = '/';
486 *p = 0; /* make strlen() below happy */
487 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
489 /* create a lower cased version of the name */
490 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
491 strcpyW(name_lwr, name);
492 strlwrW(name_lwr);
493 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name_lwr, -1,
494 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
495 HeapFree(GetProcessHeap(), 0, name_lwr);
497 file = fopen( buffer, "w" );
498 unix_name = buffer;
501 if (!file)
503 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
504 return FALSE;
507 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
508 PROFILE_Save( file, CurProfile->section );
509 fclose( file );
510 CurProfile->changed = FALSE;
511 if(!stat(unix_name,&buf))
512 CurProfile->mtime=buf.st_mtime;
513 return TRUE;
517 /***********************************************************************
518 * PROFILE_ReleaseFile
520 * Flush the current profile to disk and remove it from the cache.
522 static void PROFILE_ReleaseFile(void)
524 PROFILE_FlushFile();
525 PROFILE_Free( CurProfile->section );
526 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
527 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
528 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
529 CurProfile->changed = FALSE;
530 CurProfile->section = NULL;
531 CurProfile->dos_name = NULL;
532 CurProfile->unix_name = NULL;
533 CurProfile->filename = NULL;
534 CurProfile->mtime = 0;
538 /***********************************************************************
539 * PROFILE_Open
541 * Open a profile file, checking the cached file first.
543 static BOOL PROFILE_Open( LPCWSTR filename )
545 DOS_FULL_NAME full_name;
546 char buffer[MAX_PATHNAME_LEN];
547 WCHAR *newdos_name;
548 WCHAR *name, *name_lwr;
549 char *p;
550 FILE *file = NULL;
551 int i,j;
552 struct stat buf;
553 PROFILE *tempProfile;
555 /* First time around */
557 if(!CurProfile)
558 for(i=0;i<N_CACHED_PROFILES;i++)
560 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
561 if(MRUProfile[i] == NULL) break;
562 MRUProfile[i]->changed=FALSE;
563 MRUProfile[i]->section=NULL;
564 MRUProfile[i]->dos_name=NULL;
565 MRUProfile[i]->unix_name=NULL;
566 MRUProfile[i]->filename=NULL;
567 MRUProfile[i]->mtime=0;
570 /* Check for a match */
572 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
573 strchrW( filename, ':' ))
575 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
577 else
579 static const WCHAR bkslashW[] = {'\\',0};
580 WCHAR windirW[MAX_PATH];
582 GetWindowsDirectoryW( windirW, MAX_PATH );
583 strcatW( windirW, bkslashW );
584 strcatW( windirW, filename );
585 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
588 for(i=0;i<N_CACHED_PROFILES;i++)
590 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
591 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
593 if(i)
595 PROFILE_FlushFile();
596 tempProfile=MRUProfile[i];
597 for(j=i;j>0;j--)
598 MRUProfile[j]=MRUProfile[j-1];
599 CurProfile=tempProfile;
601 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
602 TRACE("(%s): already opened (mru=%d)\n",
603 debugstr_w(filename), i );
604 else
605 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
606 debugstr_w(filename), i );
607 return TRUE;
611 /* Flush the old current profile */
612 PROFILE_FlushFile();
614 /* Make the oldest profile the current one only in order to get rid of it */
615 if(i==N_CACHED_PROFILES)
617 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
618 for(i=N_CACHED_PROFILES-1;i>0;i--)
619 MRUProfile[i]=MRUProfile[i-1];
620 CurProfile=tempProfile;
622 if(CurProfile->filename) PROFILE_ReleaseFile();
624 /* OK, now that CurProfile is definitely free we assign it our new file */
625 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
626 strcpyW( newdos_name, full_name.short_name );
627 CurProfile->dos_name = newdos_name;
628 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
629 strcpyW( CurProfile->filename, filename );
631 /* Try to open the profile file, first in $HOME/.wine */
633 /* FIXME: this will need a more general solution */
634 strcpy( buffer, wine_get_config_dir() );
635 p = buffer + strlen(buffer);
636 *p++ = '/';
637 *p = 0; /* make strlen() below happy */
638 name = strrchrW( newdos_name, '\\' ) + 1;
640 /* create a lower cased version of the name */
641 name_lwr = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1) * sizeof(WCHAR));
642 strcpyW(name_lwr, name);
643 strlwrW(name_lwr);
644 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name_lwr, -1,
645 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
646 HeapFree(GetProcessHeap(), 0, name_lwr);
648 if ((file = fopen( buffer, "r" )))
650 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
651 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
652 strcpy( CurProfile->unix_name, buffer );
654 else
656 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
657 strcpy( CurProfile->unix_name, full_name.long_name );
658 if ((file = fopen( full_name.long_name, "r" )))
659 TRACE("(%s): found it in %s\n",
660 debugstr_w(filename), full_name.long_name );
663 if (file)
665 CurProfile->section = PROFILE_Load( file );
666 fclose( file );
667 if(!stat(CurProfile->unix_name,&buf))
668 CurProfile->mtime=buf.st_mtime;
670 else
672 /* Does not exist yet, we will create it in PROFILE_FlushFile */
673 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
675 return TRUE;
679 /***********************************************************************
680 * PROFILE_GetSection
682 * Returns all keys of a section.
683 * If return_values is TRUE, also include the corresponding values.
685 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
686 LPWSTR buffer, UINT len, BOOL handle_env,
687 BOOL return_values )
689 PROFILEKEY *key;
691 if(!buffer) return 0;
693 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
695 while (section)
697 if (section->name[0] && !strcmpiW( section->name, section_name ))
699 UINT oldlen = len;
700 for (key = section->key; key; key = key->next)
702 if (len <= 2) break;
703 if (!*key->name) continue; /* Skip empty lines */
704 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
705 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
706 len -= strlenW(buffer) + 1;
707 buffer += strlenW(buffer) + 1;
708 if (len < 2)
709 break;
710 if (return_values && key->value) {
711 buffer[-1] = '=';
712 PROFILE_CopyEntry ( buffer,
713 key->value, len - 1, handle_env, 0 );
714 len -= strlenW(buffer) + 1;
715 buffer += strlenW(buffer) + 1;
718 *buffer = '\0';
719 if (len <= 1)
720 /*If either lpszSection or lpszKey is NULL and the supplied
721 destination buffer is too small to hold all the strings,
722 the last string is truncated and followed by two null characters.
723 In this case, the return value is equal to cchReturnBuffer
724 minus two. */
726 buffer[-1] = '\0';
727 return oldlen - 2;
729 return oldlen - len;
731 section = section->next;
733 buffer[0] = buffer[1] = '\0';
734 return 0;
737 /* See GetPrivateProfileSectionNamesA for documentation */
738 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
740 LPWSTR buf;
741 UINT f,l;
742 PROFILESECTION *section;
744 if (!buffer || !len)
745 return 0;
746 if (len==1) {
747 *buffer='\0';
748 return 0;
751 f=len-1;
752 buf=buffer;
753 section = CurProfile->section;
754 while ((section!=NULL)) {
755 if (section->name[0]) {
756 l = strlenW(section->name)+1;
757 if (l > f) {
758 if (f>0) {
759 strncpyW(buf, section->name, f-1);
760 buf += f-1;
761 *buf++='\0';
763 *buf='\0';
764 return len-2;
766 strcpyW(buf, section->name);
767 buf += l;
768 f -= l;
770 section = section->next;
772 *buf='\0';
773 return buf-buffer;
777 /***********************************************************************
778 * PROFILE_GetString
780 * Get a profile string.
782 * Tests with GetPrivateProfileString16, W95a,
783 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
784 * section key_name def_val res buffer
785 * "set1" "1" "x" 43 [data]
786 * "set1" "1 " "x" 43 [data] (!)
787 * "set1" " 1 "' "x" 43 [data] (!)
788 * "set1" "" "x" 1 "x"
789 * "set1" "" "x " 1 "x" (!)
790 * "set1" "" " x " 3 " x" (!)
791 * "set1" NULL "x" 6 "1\02\03\0\0"
792 * "set1" "" "x" 1 "x"
793 * NULL "1" "x" 0 "" (!)
794 * "" "1" "x" 1 "x"
795 * NULL NULL "" 0 ""
799 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
800 LPCWSTR def_val, LPWSTR buffer, UINT len )
802 PROFILEKEY *key = NULL;
803 static const WCHAR empty_strW[] = { 0 };
805 if(!buffer) return 0;
807 if (!def_val) def_val = empty_strW;
808 if (key_name)
810 if (!key_name[0])
812 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
813 return 0;
815 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
816 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
817 len, FALSE, TRUE );
818 TRACE("(%s,%s,%s): returning %s\n",
819 debugstr_w(section), debugstr_w(key_name),
820 debugstr_w(def_val), debugstr_w(buffer) );
821 return strlenW( buffer );
823 /* no "else" here ! */
824 if (section && section[0])
826 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
827 if (!buffer[0]) /* no luck -> def_val */
829 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
830 ret = strlenW(buffer);
832 return ret;
834 buffer[0] = '\0';
835 return 0;
839 /***********************************************************************
840 * PROFILE_SetString
842 * Set a profile string.
844 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
845 LPCWSTR value, BOOL create_always )
847 if (!key_name) /* Delete a whole section */
849 TRACE("(%s)\n", debugstr_w(section_name));
850 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
851 section_name );
852 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
853 this is not an error on application's level.*/
855 else if (!value) /* Delete a key */
857 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
858 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
859 section_name, key_name );
860 return TRUE; /* same error handling as above */
862 else /* Set the key value */
864 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
865 key_name, TRUE, create_always );
866 TRACE("(%s,%s,%s):\n",
867 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
868 if (!key) return FALSE;
869 if (key->value)
871 /* strip the leading spaces. We can safely strip \n\r and
872 * friends too, they should not happen here anyway. */
873 while (PROFILE_isspace(*value)) value++;
875 if (!strcmpW( key->value, value ))
877 TRACE(" no change needed\n" );
878 return TRUE; /* No change needed */
880 TRACE(" replacing %s\n", debugstr_w(key->value) );
881 HeapFree( GetProcessHeap(), 0, key->value );
883 else TRACE(" creating key\n" );
884 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
885 strcpyW( key->value, value );
886 CurProfile->changed = TRUE;
888 return TRUE;
892 /***********************************************************************
893 * get_profile_key
895 static HKEY get_profile_key(void)
897 static HKEY profile_key;
899 if (!profile_key)
901 OBJECT_ATTRIBUTES attr;
902 UNICODE_STRING nameW;
903 HKEY hkey;
905 attr.Length = sizeof(attr);
906 attr.RootDirectory = 0;
907 attr.ObjectName = &nameW;
908 attr.Attributes = 0;
909 attr.SecurityDescriptor = NULL;
910 attr.SecurityQualityOfService = NULL;
912 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
913 NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL ))
915 ERR("Cannot create config registry key\n" );
916 ExitProcess( 1 );
918 RtlFreeUnicodeString( &nameW );
920 if (InterlockedCompareExchangePointer( (void **)&profile_key, hkey, 0 ))
921 NtClose( hkey ); /* somebody beat us to it */
923 return profile_key;
927 /***********************************************************************
928 * PROFILE_GetWineIniString
930 * Get a config string from the wine.ini file.
932 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
933 LPCWSTR def, LPWSTR buffer, int len )
935 HKEY hkey;
936 NTSTATUS err;
937 OBJECT_ATTRIBUTES attr;
938 UNICODE_STRING nameW;
940 attr.Length = sizeof(attr);
941 attr.RootDirectory = get_profile_key();
942 attr.ObjectName = &nameW;
943 attr.Attributes = 0;
944 attr.SecurityDescriptor = NULL;
945 attr.SecurityQualityOfService = NULL;
946 RtlInitUnicodeString( &nameW, section );
947 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
949 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
950 DWORD count;
952 RtlInitUnicodeString( &nameW, key_name );
953 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
954 tmp, sizeof(tmp), &count )))
956 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
957 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
959 NtClose( hkey );
962 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
963 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
964 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
965 return strlenW(buffer);
969 /******************************************************************************
971 * PROFILE_GetWineIniBool
973 * Reads a boolean value from the wine.ini file. This function attempts to
974 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
975 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
976 * true. Anything else results in the return of the default value.
978 * This function uses 1 to indicate true, and 0 for false. You can check
979 * for existence by setting def to something other than 0 or 1 and
980 * examining the return value.
982 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
984 static const WCHAR def_valueW[] = {'~',0};
985 WCHAR key_value[2];
986 int retval;
988 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
990 switch(key_value[0]) {
991 case 'n':
992 case 'N':
993 case 'f':
994 case 'F':
995 case '0':
996 retval = 0;
997 break;
999 case 'y':
1000 case 'Y':
1001 case 't':
1002 case 'T':
1003 case '1':
1004 retval = 1;
1005 break;
1007 default:
1008 retval = def;
1011 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1012 def ? "TRUE" : "FALSE", key_value[0],
1013 retval ? "TRUE" : "FALSE");
1015 return retval;
1019 /***********************************************************************
1020 * PROFILE_UsageWineIni
1022 * Explain the wine.ini file to those who don't read documentation.
1023 * Keep below one screenful in length so that error messages above are
1024 * noticed.
1026 void PROFILE_UsageWineIni(void)
1028 MESSAGE("Perhaps you have not properly edited or created "
1029 "your Wine configuration file.\n");
1030 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1031 /* RTFM, so to say */
1035 /********************* API functions **********************************/
1037 /***********************************************************************
1038 * GetProfileInt (KERNEL.57)
1040 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1042 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1046 /***********************************************************************
1047 * GetProfileIntA (KERNEL32.@)
1049 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1051 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1054 /***********************************************************************
1055 * GetProfileIntW (KERNEL32.@)
1057 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1059 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1063 * if allow_section_name_copy is TRUE, allow the copying :
1064 * - of Section names if 'section' is NULL
1065 * - of Keys in a Section if 'entry' is NULL
1066 * (see MSDN doc for GetPrivateProfileString)
1068 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1069 LPCWSTR def_val, LPWSTR buffer,
1070 UINT len, LPCWSTR filename,
1071 BOOL allow_section_name_copy )
1073 int ret;
1074 LPWSTR pDefVal = NULL;
1076 if (!filename)
1077 filename = wininiW;
1079 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1080 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1082 /* strip any trailing ' ' of def_val. */
1083 if (def_val)
1085 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1087 while (p > def_val)
1089 p--;
1090 if ((*p) != ' ')
1091 break;
1093 if (*p == ' ') /* ouch, contained trailing ' ' */
1095 int len = (int)(p - def_val);
1096 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1097 strncpyW(pDefVal, def_val, len);
1098 pDefVal[len] = '\0';
1101 if (!pDefVal)
1102 pDefVal = (LPWSTR)def_val;
1104 EnterCriticalSection( &PROFILE_CritSect );
1106 if (PROFILE_Open( filename )) {
1107 if ((allow_section_name_copy) && (section == NULL))
1108 ret = PROFILE_GetSectionNames(buffer, len);
1109 else
1110 /* PROFILE_GetString already handles the 'entry == NULL' case */
1111 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1112 } else {
1113 lstrcpynW( buffer, pDefVal, len );
1114 ret = strlenW( buffer );
1117 LeaveCriticalSection( &PROFILE_CritSect );
1119 if (pDefVal != def_val) /* allocated */
1120 HeapFree(GetProcessHeap(), 0, pDefVal);
1122 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1124 return ret;
1127 /***********************************************************************
1128 * GetPrivateProfileString (KERNEL.128)
1130 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1131 LPCSTR def_val, LPSTR buffer,
1132 UINT16 len, LPCSTR filename )
1134 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1135 LPWSTR bufferW;
1136 INT16 retW, ret = 0;
1138 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1139 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1140 else sectionW.Buffer = NULL;
1141 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1142 else entryW.Buffer = NULL;
1143 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1144 else def_valW.Buffer = NULL;
1145 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1146 else filenameW.Buffer = NULL;
1148 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1149 def_valW.Buffer, bufferW, len,
1150 filenameW.Buffer, FALSE );
1151 if (len)
1153 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1154 if (!ret)
1156 ret = len - 1;
1157 buffer[ret] = 0;
1159 else
1160 ret--; /* strip terminating 0 */
1163 RtlFreeUnicodeString(&sectionW);
1164 RtlFreeUnicodeString(&entryW);
1165 RtlFreeUnicodeString(&def_valW);
1166 RtlFreeUnicodeString(&filenameW);
1167 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1168 return ret;
1171 /***********************************************************************
1172 * GetPrivateProfileStringA (KERNEL32.@)
1174 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1175 LPCSTR def_val, LPSTR buffer,
1176 UINT len, LPCSTR filename )
1178 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1179 LPWSTR bufferW;
1180 INT retW, ret = 0;
1182 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1183 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1184 else sectionW.Buffer = NULL;
1185 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1186 else entryW.Buffer = NULL;
1187 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1188 else def_valW.Buffer = NULL;
1189 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1190 else filenameW.Buffer = NULL;
1192 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1193 def_valW.Buffer, bufferW, len,
1194 filenameW.Buffer);
1195 if (len)
1197 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1198 if (!ret)
1200 ret = len - 1;
1201 buffer[ret] = 0;
1203 else
1204 ret--; /* strip terminating 0 */
1207 RtlFreeUnicodeString(&sectionW);
1208 RtlFreeUnicodeString(&entryW);
1209 RtlFreeUnicodeString(&def_valW);
1210 RtlFreeUnicodeString(&filenameW);
1211 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1212 return ret;
1215 /***********************************************************************
1216 * GetPrivateProfileStringW (KERNEL32.@)
1218 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1219 LPCWSTR def_val, LPWSTR buffer,
1220 UINT len, LPCWSTR filename )
1222 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1223 buffer, len, filename, TRUE );
1226 /***********************************************************************
1227 * GetProfileString (KERNEL.58)
1229 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1230 LPSTR buffer, UINT16 len )
1232 return GetPrivateProfileString16( section, entry, def_val,
1233 buffer, len, "win.ini" );
1236 /***********************************************************************
1237 * GetProfileStringA (KERNEL32.@)
1239 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1240 LPSTR buffer, UINT len )
1242 return GetPrivateProfileStringA( section, entry, def_val,
1243 buffer, len, "win.ini" );
1246 /***********************************************************************
1247 * GetProfileStringW (KERNEL32.@)
1249 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1250 LPCWSTR def_val, LPWSTR buffer, UINT len )
1252 return GetPrivateProfileStringW( section, entry, def_val,
1253 buffer, len, wininiW );
1256 /***********************************************************************
1257 * WriteProfileString (KERNEL.59)
1259 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1260 LPCSTR string )
1262 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1265 /***********************************************************************
1266 * WriteProfileStringA (KERNEL32.@)
1268 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1269 LPCSTR string )
1271 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1274 /***********************************************************************
1275 * WriteProfileStringW (KERNEL32.@)
1277 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1278 LPCWSTR string )
1280 return WritePrivateProfileStringW( section, entry, string, wininiW );
1284 /***********************************************************************
1285 * GetPrivateProfileInt (KERNEL.127)
1287 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1288 INT16 def_val, LPCSTR filename )
1290 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1291 * here, but Win98SE doesn't care about this at all, so I deleted it.
1292 * AFAIR versions prior to Win9x had these limits, though. */
1293 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1296 /***********************************************************************
1297 * GetPrivateProfileIntA (KERNEL32.@)
1299 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1300 INT def_val, LPCSTR filename )
1302 char buffer[20];
1303 long result;
1305 if (!GetPrivateProfileStringA( section, entry, "",
1306 buffer, sizeof(buffer), filename ))
1307 return def_val;
1308 /* FIXME: if entry can be found but it's empty, then Win16 is
1309 * supposed to return 0 instead of def_val ! Difficult/problematic
1310 * to implement (every other failure also returns zero buffer),
1311 * thus wait until testing framework avail for making sure nothing
1312 * else gets broken that way. */
1313 if (!buffer[0]) return (UINT)def_val;
1315 /* Don't use strtol() here !
1316 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1317 YES, scan for unsigned format ! (otherwise compatibility error) */
1318 if (!sscanf(buffer, "%lu", &result)) return 0;
1319 return (UINT)result;
1322 /***********************************************************************
1323 * GetPrivateProfileIntW (KERNEL32.@)
1325 * FIXME: rewrite using unicode
1327 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1328 INT def_val, LPCWSTR filename )
1330 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1331 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1332 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1333 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1334 HeapFree( GetProcessHeap(), 0, sectionA );
1335 HeapFree( GetProcessHeap(), 0, filenameA );
1336 HeapFree( GetProcessHeap(), 0, entryA );
1337 return res;
1340 /***********************************************************************
1341 * GetPrivateProfileSection (KERNEL.418)
1343 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1344 UINT16 len, LPCSTR filename )
1346 return GetPrivateProfileSectionA( section, buffer, len, filename );
1349 /***********************************************************************
1350 * GetPrivateProfileSectionW (KERNEL32.@)
1352 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1353 DWORD len, LPCWSTR filename )
1355 int ret = 0;
1357 EnterCriticalSection( &PROFILE_CritSect );
1359 if (PROFILE_Open( filename ))
1360 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1361 FALSE, TRUE);
1363 LeaveCriticalSection( &PROFILE_CritSect );
1365 return ret;
1368 /***********************************************************************
1369 * GetPrivateProfileSectionA (KERNEL32.@)
1371 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1372 DWORD len, LPCSTR filename )
1374 UNICODE_STRING sectionW, filenameW;
1375 LPWSTR bufferW;
1376 INT retW, ret = 0;
1378 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1379 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1380 else sectionW.Buffer = NULL;
1381 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1382 else filenameW.Buffer = NULL;
1384 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1385 if (len > 2)
1387 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1388 if (ret > 2)
1389 ret -= 2;
1390 else
1392 ret = 0;
1393 buffer[len-2] = 0;
1394 buffer[len-1] = 0;
1397 else
1399 buffer[0] = 0;
1400 buffer[1] = 0;
1403 RtlFreeUnicodeString(&sectionW);
1404 RtlFreeUnicodeString(&filenameW);
1405 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1406 return ret;
1409 /***********************************************************************
1410 * GetProfileSection (KERNEL.419)
1412 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1414 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1417 /***********************************************************************
1418 * GetProfileSectionA (KERNEL32.@)
1420 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1422 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1425 /***********************************************************************
1426 * GetProfileSectionW (KERNEL32.@)
1428 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1430 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1434 /***********************************************************************
1435 * WritePrivateProfileString (KERNEL.129)
1437 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1438 LPCSTR string, LPCSTR filename )
1440 return WritePrivateProfileStringA(section,entry,string,filename);
1443 /***********************************************************************
1444 * WritePrivateProfileStringW (KERNEL32.@)
1446 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1447 LPCWSTR string, LPCWSTR filename )
1449 BOOL ret = FALSE;
1451 EnterCriticalSection( &PROFILE_CritSect );
1453 if (PROFILE_Open( filename ))
1455 if (!section && !entry && !string) /* documented "file flush" case */
1457 PROFILE_FlushFile();
1458 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1460 else {
1461 if (!section) {
1462 FIXME("(NULL?,%s,%s,%s)?\n",
1463 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1464 } else {
1465 ret = PROFILE_SetString( section, entry, string, FALSE);
1466 PROFILE_FlushFile();
1471 LeaveCriticalSection( &PROFILE_CritSect );
1472 return ret;
1475 /***********************************************************************
1476 * WritePrivateProfileStringA (KERNEL32.@)
1478 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1479 LPCSTR string, LPCSTR filename )
1481 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1482 BOOL ret;
1484 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1485 else sectionW.Buffer = NULL;
1486 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1487 else entryW.Buffer = NULL;
1488 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1489 else stringW.Buffer = NULL;
1490 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1491 else filenameW.Buffer = NULL;
1493 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1494 stringW.Buffer, filenameW.Buffer);
1495 RtlFreeUnicodeString(&sectionW);
1496 RtlFreeUnicodeString(&entryW);
1497 RtlFreeUnicodeString(&stringW);
1498 RtlFreeUnicodeString(&filenameW);
1499 return ret;
1502 /***********************************************************************
1503 * WritePrivateProfileSection (KERNEL.416)
1505 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1506 LPCSTR string, LPCSTR filename )
1508 return WritePrivateProfileSectionA( section, string, filename );
1511 /***********************************************************************
1512 * WritePrivateProfileSectionW (KERNEL32.@)
1514 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1515 LPCWSTR string, LPCWSTR filename )
1517 BOOL ret = FALSE;
1518 LPWSTR p;
1520 EnterCriticalSection( &PROFILE_CritSect );
1522 if (PROFILE_Open( filename )) {
1523 if (!section && !string)
1524 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1525 else if (!string) {/* delete the named section*/
1526 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1527 PROFILE_FlushFile();
1528 } else {
1529 PROFILE_DeleteAllKeys(section);
1530 ret = TRUE;
1531 while(*string) {
1532 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1533 strcpyW( buf, string );
1534 if((p = strchrW( buf, '='))) {
1535 *p='\0';
1536 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1538 HeapFree( GetProcessHeap(), 0, buf );
1539 string += strlenW(string)+1;
1541 PROFILE_FlushFile();
1545 LeaveCriticalSection( &PROFILE_CritSect );
1546 return ret;
1549 /***********************************************************************
1550 * WritePrivateProfileSectionA (KERNEL32.@)
1552 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1553 LPCSTR string, LPCSTR filename)
1556 UNICODE_STRING sectionW, filenameW;
1557 LPWSTR stringW;
1558 BOOL ret;
1560 if (string)
1562 INT lenA, lenW;
1563 LPCSTR p = string;
1565 while(*p) p += strlen(p) + 1;
1566 lenA = p - string + 1;
1567 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1568 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1569 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1571 else stringW = NULL;
1572 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1573 else sectionW.Buffer = NULL;
1574 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1575 else filenameW.Buffer = NULL;
1577 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1579 HeapFree(GetProcessHeap(), 0, stringW);
1580 RtlFreeUnicodeString(&sectionW);
1581 RtlFreeUnicodeString(&filenameW);
1582 return ret;
1585 /***********************************************************************
1586 * WriteProfileSection (KERNEL.417)
1588 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1590 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1593 /***********************************************************************
1594 * WriteProfileSectionA (KERNEL32.@)
1596 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1599 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1602 /***********************************************************************
1603 * WriteProfileSectionW (KERNEL32.@)
1605 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1607 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1610 /***********************************************************************
1611 * GetPrivateProfileSectionNames (KERNEL.143)
1613 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1614 LPCSTR filename )
1616 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1620 /***********************************************************************
1621 * GetProfileSectionNames (KERNEL.142)
1623 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1626 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1630 /***********************************************************************
1631 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1633 * Returns the section names contained in the specified file.
1634 * FIXME: Where do we find this file when the path is relative?
1635 * The section names are returned as a list of strings with an extra
1636 * '\0' to mark the end of the list. Except for that the behavior
1637 * depends on the Windows version.
1639 * Win95:
1640 * - if the buffer is 0 or 1 character long then it is as if it was of
1641 * infinite length.
1642 * - otherwise, if the buffer is to small only the section names that fit
1643 * are returned.
1644 * - note that this means if the buffer was to small to return even just
1645 * the first section name then a single '\0' will be returned.
1646 * - the return value is the number of characters written in the buffer,
1647 * except if the buffer was too smal in which case len-2 is returned
1649 * Win2000:
1650 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1651 * '\0' and the return value is 0
1652 * - otherwise if the buffer is too small then the first section name that
1653 * does not fit is truncated so that the string list can be terminated
1654 * correctly (double '\0')
1655 * - the return value is the number of characters written in the buffer
1656 * except for the trailing '\0'. If the buffer is too small, then the
1657 * return value is len-2
1658 * - Win2000 has a bug that triggers when the section names and the
1659 * trailing '\0' fit exactly in the buffer. In that case the trailing
1660 * '\0' is missing.
1662 * Wine implements the observed Win2000 behavior (except for the bug).
1664 * Note that when the buffer is big enough then the return value may be any
1665 * value between 1 and len-1 (or len in Win95), including len-2.
1667 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1668 LPCWSTR filename)
1670 DWORD ret = 0;
1672 EnterCriticalSection( &PROFILE_CritSect );
1674 if (PROFILE_Open( filename ))
1675 ret = PROFILE_GetSectionNames(buffer, size);
1677 LeaveCriticalSection( &PROFILE_CritSect );
1679 return ret;
1683 /***********************************************************************
1684 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1686 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1687 LPCSTR filename)
1689 UNICODE_STRING filenameW;
1690 LPWSTR bufferW;
1691 INT retW, ret = 0;
1693 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1694 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1695 else filenameW.Buffer = NULL;
1697 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1698 if (retW && size)
1700 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1701 if (!ret)
1703 ret = size;
1704 buffer[size-1] = 0;
1708 RtlFreeUnicodeString(&filenameW);
1709 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1710 return ret;
1713 /***********************************************************************
1714 * GetPrivateProfileStruct (KERNEL.407)
1716 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1717 LPVOID buf, UINT16 len, LPCSTR filename)
1719 return GetPrivateProfileStructA( section, key, buf, len, filename );
1722 /***********************************************************************
1723 * GetPrivateProfileStructW (KERNEL32.@)
1725 * Should match Win95's behaviour pretty much
1727 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1728 LPVOID buf, UINT len, LPCWSTR filename)
1730 BOOL ret = FALSE;
1732 EnterCriticalSection( &PROFILE_CritSect );
1734 if (PROFILE_Open( filename )) {
1735 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1736 if (k) {
1737 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1738 if (((strlenW(k->value) - 2) / 2) == len)
1740 LPWSTR end, p;
1741 BOOL valid = TRUE;
1742 WCHAR c;
1743 DWORD chksum = 0;
1745 end = k->value + strlenW(k->value); /* -> '\0' */
1746 /* check for invalid chars in ASCII coded hex string */
1747 for (p=k->value; p < end; p++)
1749 if (!isxdigitW(*p))
1751 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1752 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1753 valid = FALSE;
1754 break;
1757 if (valid)
1759 BOOL highnibble = TRUE;
1760 BYTE b = 0, val;
1761 LPBYTE binbuf = (LPBYTE)buf;
1763 end -= 2; /* don't include checksum in output data */
1764 /* translate ASCII hex format into binary data */
1765 for (p=k->value; p < end; p++)
1767 c = toupperW(*p);
1768 val = (c > '9') ?
1769 (c - 'A' + 10) : (c - '0');
1771 if (highnibble)
1772 b = val << 4;
1773 else
1775 b += val;
1776 *binbuf++ = b; /* feed binary data into output */
1777 chksum += b; /* calculate checksum */
1779 highnibble ^= 1; /* toggle */
1781 /* retrieve stored checksum value */
1782 c = toupperW(*p++);
1783 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1784 c = toupperW(*p);
1785 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1786 if (b == (chksum & 0xff)) /* checksums match ? */
1787 ret = TRUE;
1792 LeaveCriticalSection( &PROFILE_CritSect );
1794 return ret;
1797 /***********************************************************************
1798 * GetPrivateProfileStructA (KERNEL32.@)
1800 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1801 LPVOID buffer, UINT len, LPCSTR filename)
1803 UNICODE_STRING sectionW, keyW, filenameW;
1804 INT ret;
1806 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1807 else sectionW.Buffer = NULL;
1808 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1809 else keyW.Buffer = NULL;
1810 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1811 else filenameW.Buffer = NULL;
1813 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1814 filenameW.Buffer);
1815 /* Do not translate binary data. */
1817 RtlFreeUnicodeString(&sectionW);
1818 RtlFreeUnicodeString(&keyW);
1819 RtlFreeUnicodeString(&filenameW);
1820 return ret;
1825 /***********************************************************************
1826 * WritePrivateProfileStruct (KERNEL.406)
1828 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1829 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1831 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1834 /***********************************************************************
1835 * WritePrivateProfileStructW (KERNEL32.@)
1837 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1838 LPVOID buf, UINT bufsize, LPCWSTR filename)
1840 BOOL ret = FALSE;
1841 LPBYTE binbuf;
1842 LPWSTR outstring, p;
1843 DWORD sum = 0;
1845 if (!section && !key && !buf) /* flush the cache */
1846 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1848 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1849 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1850 p = outstring;
1851 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1852 *p++ = hex[*binbuf >> 4];
1853 *p++ = hex[*binbuf & 0xf];
1854 sum += *binbuf;
1856 /* checksum is sum & 0xff */
1857 *p++ = hex[(sum & 0xf0) >> 4];
1858 *p++ = hex[sum & 0xf];
1859 *p++ = '\0';
1861 EnterCriticalSection( &PROFILE_CritSect );
1863 if (PROFILE_Open( filename )) {
1864 ret = PROFILE_SetString( section, key, outstring, FALSE);
1865 PROFILE_FlushFile();
1868 LeaveCriticalSection( &PROFILE_CritSect );
1870 HeapFree( GetProcessHeap(), 0, outstring );
1872 return ret;
1875 /***********************************************************************
1876 * WritePrivateProfileStructA (KERNEL32.@)
1878 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1879 LPVOID buf, UINT bufsize, LPCSTR filename)
1881 UNICODE_STRING sectionW, keyW, filenameW;
1882 INT ret;
1884 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1885 else sectionW.Buffer = NULL;
1886 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1887 else keyW.Buffer = NULL;
1888 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1889 else filenameW.Buffer = NULL;
1891 /* Do not translate binary data. */
1892 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
1893 filenameW.Buffer);
1895 RtlFreeUnicodeString(&sectionW);
1896 RtlFreeUnicodeString(&keyW);
1897 RtlFreeUnicodeString(&filenameW);
1898 return ret;
1902 /***********************************************************************
1903 * WriteOutProfiles (KERNEL.315)
1905 void WINAPI WriteOutProfiles16(void)
1907 EnterCriticalSection( &PROFILE_CritSect );
1908 PROFILE_FlushFile();
1909 LeaveCriticalSection( &PROFILE_CritSect );
1912 /***********************************************************************
1913 * CloseProfileUserMapping (KERNEL32.@)
1915 BOOL WINAPI CloseProfileUserMapping(void) {
1916 FIXME("(), stub!\n");
1917 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1918 return FALSE;