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