Import wine_tsx11_lock/unlock directly from x11drv in opengl32 and
[wine/multimedia.git] / files / profile.c
blob1ade17892e8fcc9f850998266d39950201c41ff6
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
23 #include "wine/port.h"
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winerror.h"
41 #include "winternl.h"
42 #include "wine/winbase16.h"
43 #include "drive.h"
44 #include "file.h"
45 #include "heap.h"
46 #include "wine/unicode.h"
47 #include "wine/server.h"
48 #include "wine/library.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(profile);
53 typedef struct tagPROFILEKEY
55 WCHAR *value;
56 struct tagPROFILEKEY *next;
57 WCHAR name[1];
58 } PROFILEKEY;
60 typedef struct tagPROFILESECTION
62 struct tagPROFILEKEY *key;
63 struct tagPROFILESECTION *next;
64 WCHAR name[1];
65 } PROFILESECTION;
68 typedef struct
70 BOOL changed;
71 PROFILESECTION *section;
72 WCHAR *dos_name;
73 char *unix_name;
74 WCHAR *filename;
75 time_t mtime;
76 } PROFILE;
79 #define N_CACHED_PROFILES 10
81 /* Cached profile files */
82 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
84 #define CurProfile (MRUProfile[0])
86 /* wine.ini config file registry root */
87 static HKEY wine_profile_key;
89 #define PROFILE_MAX_LINE_LEN 1024
91 /* Wine profile name in $HOME directory; must begin with slash */
92 static const char PROFILE_WineIniName[] = "/.winerc";
94 /* Wine profile: the profile file being used */
95 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
97 /* Check for comments in profile */
98 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
100 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
102 static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
104 static const char hex[16] = "0123456789ABCDEF";
106 /***********************************************************************
107 * PROFILE_CopyEntry
109 * Copy the content of an entry into a buffer, removing quotes, and possibly
110 * translating environment variables.
112 static void PROFILE_CopyEntry( LPWSTR buffer, LPCWSTR value, int len,
113 int handle_env, BOOL strip_quote )
115 WCHAR quote = '\0';
116 LPCWSTR p;
118 if(!buffer) return;
120 if (strip_quote && ((*value == '\'') || (*value == '\"')))
122 if (value[1] && (value[strlenW(value)-1] == *value)) quote = *value++;
125 if (!handle_env)
127 lstrcpynW( buffer, value, len );
128 if (quote && (len >= strlenW(value))) buffer[strlenW(buffer)-1] = '\0';
129 return;
132 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
134 if ((*p == '$') && (p[1] == '{'))
136 WCHAR env_val[1024];
137 LPCWSTR p2 = strchrW( p, '}' );
138 int copy_len;
139 if (!p2) continue; /* ignore it */
140 copy_len = min( 1024, (int)(p2-p)-1 );
141 strncpyW(env_val, p + 2, copy_len );
142 env_val[copy_len - 1] = 0; /* ensure 0 termination */
143 *buffer = 0;
144 if (GetEnvironmentVariableW( env_val, buffer, len))
146 copy_len = strlenW( buffer );
147 buffer += copy_len;
148 len -= copy_len;
150 p = p2 + 1;
153 if (quote && (len > 1)) buffer--;
154 *buffer = '\0';
158 /***********************************************************************
159 * PROFILE_Save
161 * Save a profile tree to a file.
163 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
165 PROFILEKEY *key;
166 char buffer[PROFILE_MAX_LINE_LEN];
168 for ( ; section; section = section->next)
170 if (section->name[0])
172 WideCharToMultiByte(CP_ACP, 0, section->name, -1, buffer, sizeof(buffer), NULL, NULL);
173 fprintf( file, "\r\n[%s]\r\n", buffer );
175 for (key = section->key; key; key = key->next)
177 WideCharToMultiByte(CP_ACP, 0, key->name, -1, buffer, sizeof(buffer), NULL, NULL);
178 fprintf( file, "%s", buffer );
179 if (key->value)
181 WideCharToMultiByte(CP_ACP, 0, key->value, -1, buffer, sizeof(buffer), NULL, NULL);
182 fprintf( file, "=%s", buffer );
184 fprintf( file, "\r\n" );
190 /***********************************************************************
191 * PROFILE_Free
193 * Free a profile tree.
195 static void PROFILE_Free( PROFILESECTION *section )
197 PROFILESECTION *next_section;
198 PROFILEKEY *key, *next_key;
200 for ( ; section; section = next_section)
202 for (key = section->key; key; key = next_key)
204 next_key = key->next;
205 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
206 HeapFree( GetProcessHeap(), 0, key );
208 next_section = section->next;
209 HeapFree( GetProcessHeap(), 0, section );
213 static inline int PROFILE_isspace(char c)
215 if (isspace(c)) return 1;
216 if (c=='\r' || c==0x1a) return 1;
217 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
218 return 0;
222 /***********************************************************************
223 * PROFILE_Load
225 * Load a profile tree from a file.
227 static PROFILESECTION *PROFILE_Load( FILE *file )
229 char buffer[PROFILE_MAX_LINE_LEN];
230 char *p, *p2;
231 int line = 0, len;
232 PROFILESECTION *section, *first_section;
233 PROFILESECTION **next_section;
234 PROFILEKEY *key, *prev_key, **next_key;
236 first_section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) );
237 if(first_section == NULL) return NULL;
238 first_section->name[0] = 0;
239 first_section->key = NULL;
240 first_section->next = NULL;
241 next_section = &first_section->next;
242 next_key = &first_section->key;
243 prev_key = NULL;
245 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
247 line++;
248 p = buffer;
249 while (*p && PROFILE_isspace(*p)) p++;
250 if (*p == '[') /* section start */
252 if (!(p2 = strrchr( p, ']' )))
254 WARN("Invalid section header at line %d: '%s'\n",
255 line, p );
257 else
259 *p2 = '\0';
260 p++;
261 len = strlen(p);
262 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR) )))
263 break;
264 MultiByteToWideChar(CP_ACP, 0, p, -1, section->name, len + 1);
265 section->key = NULL;
266 section->next = NULL;
267 *next_section = section;
268 next_section = &section->next;
269 next_key = &section->key;
270 prev_key = NULL;
272 TRACE("New section: %s\n", debugstr_w(section->name));
274 continue;
278 p2=p+strlen(p) - 1;
279 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
281 if ((p2 = strchr( p, '=' )) != NULL)
283 char *p3 = p2 - 1;
284 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
285 *p2++ = '\0';
286 while (*p2 && PROFILE_isspace(*p2)) p2++;
289 if(*p || !prev_key || *prev_key->name)
291 len = strlen(p);
292 if (!(key = HeapAlloc( GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR) ))) break;
293 MultiByteToWideChar(CP_ACP, 0, p, -1, key->name, len + 1);
294 if (p2)
296 len = strlen(p2) + 1;
297 key->value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
298 MultiByteToWideChar(CP_ACP, 0, p2, -1, key->value, len);
300 else key->value = NULL;
302 key->next = NULL;
303 *next_key = key;
304 next_key = &key->next;
305 prev_key = key;
307 TRACE("New key: name=%s, value=%s\n",
308 debugstr_w(key->name), key->value ? debugstr_w(key->value) : "(none)");
311 return first_section;
314 /* convert the .winerc file to the new format */
315 static void convert_config( FILE *in, const char *output_name )
317 char buffer[PROFILE_MAX_LINE_LEN];
318 char *p, *p2;
319 FILE *out;
321 /* create the output file, only if it doesn't exist already */
322 int fd = open( output_name, O_WRONLY|O_CREAT|O_EXCL, 0666 );
323 if (fd == -1)
325 MESSAGE( "Could not create new config file '%s': %s\n", output_name, strerror(errno) );
326 ExitProcess(1);
329 out = fdopen( fd, "w" );
330 fprintf( out, "WINE REGISTRY Version 2\n" );
331 fprintf( out, ";; All keys relative to \\\\Machine\\\\Software\\\\Wine\\\\Wine\\\\Config\n\n" );
332 while (fgets( buffer, PROFILE_MAX_LINE_LEN, in ))
334 if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0;
335 p = buffer;
336 while (*p && PROFILE_isspace(*p)) p++;
337 if (*p == '[') /* section start */
339 if ((p2 = strrchr( p, ']' )))
341 *p2 = '\0';
342 p++;
343 fprintf( out, "[%s]\n", p );
345 continue;
348 if (*p == ';' || *p == '#')
350 fprintf( out, "%s\n", p );
351 continue;
354 p2=p+strlen(p) - 1;
355 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
357 if ((p2 = strchr( p, '=' )) != NULL)
359 char *p3 = p2 - 1;
360 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
361 *p2++ = '\0';
362 while (*p2 && PROFILE_isspace(*p2)) p2++;
365 if (!*p)
367 fprintf( out, "\n" );
368 continue;
370 fputc( '"', out );
371 while (*p)
373 if (*p == '\\') fputc( '\\', out );
374 fputc( *p, out );
375 p++;
377 fprintf( out, "\" = \"" );
378 if (p2)
380 while (*p2)
382 if (*p2 == '\\') fputc( '\\', out );
383 fputc( *p2, out );
384 p2++;
387 fprintf( out, "\"\n" );
389 fclose( out );
393 /***********************************************************************
394 * PROFILE_DeleteSection
396 * Delete a section from a profile tree.
398 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCWSTR name )
400 while (*section)
402 if ((*section)->name[0] && !strcmpiW( (*section)->name, name ))
404 PROFILESECTION *to_del = *section;
405 *section = to_del->next;
406 to_del->next = NULL;
407 PROFILE_Free( to_del );
408 return TRUE;
410 section = &(*section)->next;
412 return FALSE;
416 /***********************************************************************
417 * PROFILE_DeleteKey
419 * Delete a key from a profile tree.
421 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
422 LPCWSTR section_name, LPCWSTR key_name )
424 while (*section)
426 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
428 PROFILEKEY **key = &(*section)->key;
429 while (*key)
431 if (!strcmpiW( (*key)->name, key_name ))
433 PROFILEKEY *to_del = *key;
434 *key = to_del->next;
435 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
436 HeapFree( GetProcessHeap(), 0, to_del );
437 return TRUE;
439 key = &(*key)->next;
442 section = &(*section)->next;
444 return FALSE;
448 /***********************************************************************
449 * PROFILE_DeleteAllKeys
451 * Delete all keys from a profile tree.
453 void PROFILE_DeleteAllKeys( LPCWSTR section_name)
455 PROFILESECTION **section= &CurProfile->section;
456 while (*section)
458 if ((*section)->name[0] && !strcmpiW( (*section)->name, section_name ))
460 PROFILEKEY **key = &(*section)->key;
461 while (*key)
463 PROFILEKEY *to_del = *key;
464 *key = to_del->next;
465 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
466 HeapFree( GetProcessHeap(), 0, to_del );
467 CurProfile->changed =TRUE;
470 section = &(*section)->next;
475 /***********************************************************************
476 * PROFILE_Find
478 * Find a key in a profile tree, optionally creating it.
480 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section, LPCWSTR section_name,
481 LPCWSTR key_name, BOOL create, BOOL create_always )
483 LPCWSTR p;
484 int seclen, keylen;
486 while (PROFILE_isspace(*section_name)) section_name++;
487 p = section_name + strlenW(section_name) - 1;
488 while ((p > section_name) && PROFILE_isspace(*p)) p--;
489 seclen = p - section_name + 1;
491 while (PROFILE_isspace(*key_name)) key_name++;
492 p = key_name + strlenW(key_name) - 1;
493 while ((p > key_name) && PROFILE_isspace(*p)) p--;
494 keylen = p - key_name + 1;
496 while (*section)
498 if ( ((*section)->name[0])
499 && (!(strncmpiW( (*section)->name, section_name, seclen )))
500 && (((*section)->name)[seclen] == '\0') )
502 PROFILEKEY **key = &(*section)->key;
504 while (*key)
506 /* If create_always is FALSE then we check if the keyname already exists.
507 * Otherwise we add it regardless of its existence, to allow
508 * keys to be added more then once in some cases.
510 if(!create_always)
512 if ( (!(strncmpiW( (*key)->name, key_name, keylen )))
513 && (((*key)->name)[keylen] == '\0') )
514 return *key;
516 key = &(*key)->next;
518 if (!create) return NULL;
519 if (!(*key = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
520 return NULL;
521 strcpyW( (*key)->name, key_name );
522 (*key)->value = NULL;
523 (*key)->next = NULL;
524 return *key;
526 section = &(*section)->next;
528 if (!create) return NULL;
529 *section = HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) + strlenW(section_name) * sizeof(WCHAR) );
530 if(*section == NULL) return NULL;
531 strcpyW( (*section)->name, section_name );
532 (*section)->next = NULL;
533 if (!((*section)->key = HeapAlloc( GetProcessHeap(), 0,
534 sizeof(PROFILEKEY) + strlenW(key_name) * sizeof(WCHAR) )))
536 HeapFree(GetProcessHeap(), 0, *section);
537 return NULL;
539 strcpyW( (*section)->key->name, key_name );
540 (*section)->key->value = NULL;
541 (*section)->key->next = NULL;
542 return (*section)->key;
546 /***********************************************************************
547 * PROFILE_FlushFile
549 * Flush the current profile to disk if changed.
551 static BOOL PROFILE_FlushFile(void)
553 char *p, buffer[MAX_PATHNAME_LEN];
554 const char *unix_name;
555 FILE *file = NULL;
556 struct stat buf;
558 if(!CurProfile)
560 WARN("No current profile!\n");
561 return FALSE;
564 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
565 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
567 int drive = toupperW(CurProfile->dos_name[0]) - 'A';
568 WCHAR *name;
569 /* Try to create it in $HOME/.wine */
570 /* FIXME: this will need a more general solution */
571 strcpy( buffer, wine_get_config_dir() );
572 p = buffer + strlen(buffer);
573 *p++ = '/';
574 *p = 0; /* make strlen() below happy */
575 name = strrchrW( CurProfile->dos_name, '\\' ) + 1;
576 WideCharToMultiByte(DRIVE_GetCodepage(drive), 0, name, -1,
577 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
578 file = fopen( buffer, "w" );
579 unix_name = buffer;
582 if (!file)
584 WARN("could not save profile file %s\n", debugstr_w(CurProfile->dos_name));
585 return FALSE;
588 TRACE("Saving %s into '%s'\n", debugstr_w(CurProfile->dos_name), unix_name );
589 PROFILE_Save( file, CurProfile->section );
590 fclose( file );
591 CurProfile->changed = FALSE;
592 if(!stat(unix_name,&buf))
593 CurProfile->mtime=buf.st_mtime;
594 return TRUE;
598 /***********************************************************************
599 * PROFILE_ReleaseFile
601 * Flush the current profile to disk and remove it from the cache.
603 static void PROFILE_ReleaseFile(void)
605 PROFILE_FlushFile();
606 PROFILE_Free( CurProfile->section );
607 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
608 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
609 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
610 CurProfile->changed = FALSE;
611 CurProfile->section = NULL;
612 CurProfile->dos_name = NULL;
613 CurProfile->unix_name = NULL;
614 CurProfile->filename = NULL;
615 CurProfile->mtime = 0;
619 /***********************************************************************
620 * PROFILE_Open
622 * Open a profile file, checking the cached file first.
624 static BOOL PROFILE_Open( LPCWSTR filename )
626 DOS_FULL_NAME full_name;
627 char buffer[MAX_PATHNAME_LEN];
628 WCHAR *newdos_name;
629 WCHAR *name;
630 char *p;
631 FILE *file = NULL;
632 int i,j;
633 struct stat buf;
634 PROFILE *tempProfile;
636 /* First time around */
638 if(!CurProfile)
639 for(i=0;i<N_CACHED_PROFILES;i++)
641 MRUProfile[i]=HeapAlloc( GetProcessHeap(), 0, sizeof(PROFILE) );
642 if(MRUProfile[i] == NULL) break;
643 MRUProfile[i]->changed=FALSE;
644 MRUProfile[i]->section=NULL;
645 MRUProfile[i]->dos_name=NULL;
646 MRUProfile[i]->unix_name=NULL;
647 MRUProfile[i]->filename=NULL;
648 MRUProfile[i]->mtime=0;
651 /* Check for a match */
653 if (strchrW( filename, '/' ) || strchrW( filename, '\\' ) ||
654 strchrW( filename, ':' ))
656 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
658 else
660 static const WCHAR bkslashW[] = {'\\',0};
661 WCHAR windirW[MAX_PATH];
663 GetWindowsDirectoryW( windirW, MAX_PATH );
664 strcatW( windirW, bkslashW );
665 strcatW( windirW, filename );
666 if (!DOSFS_GetFullName( windirW, FALSE, &full_name )) return FALSE;
669 for(i=0;i<N_CACHED_PROFILES;i++)
671 if ((MRUProfile[i]->filename && !strcmpW( filename, MRUProfile[i]->filename )) ||
672 (MRUProfile[i]->dos_name && !strcmpW( full_name.short_name, MRUProfile[i]->dos_name )))
674 if(i)
676 PROFILE_FlushFile();
677 tempProfile=MRUProfile[i];
678 for(j=i;j>0;j--)
679 MRUProfile[j]=MRUProfile[j-1];
680 CurProfile=tempProfile;
682 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
683 TRACE("(%s): already opened (mru=%d)\n",
684 debugstr_w(filename), i );
685 else
686 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
687 debugstr_w(filename), i );
688 return TRUE;
692 /* Flush the old current profile */
693 PROFILE_FlushFile();
695 /* Make the oldest profile the current one only in order to get rid of it */
696 if(i==N_CACHED_PROFILES)
698 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
699 for(i=N_CACHED_PROFILES-1;i>0;i--)
700 MRUProfile[i]=MRUProfile[i-1];
701 CurProfile=tempProfile;
703 if(CurProfile->filename) PROFILE_ReleaseFile();
705 /* OK, now that CurProfile is definitely free we assign it our new file */
706 newdos_name = HeapAlloc( GetProcessHeap(), 0, (strlenW(full_name.short_name)+1) * sizeof(WCHAR) );
707 strcpyW( newdos_name, full_name.short_name );
708 CurProfile->dos_name = newdos_name;
709 CurProfile->filename = HeapAlloc( GetProcessHeap(), 0, (strlenW(filename)+1) * sizeof(WCHAR) );
710 strcpyW( CurProfile->filename, filename );
712 /* Try to open the profile file, first in $HOME/.wine */
714 /* FIXME: this will need a more general solution */
715 strcpy( buffer, wine_get_config_dir() );
716 p = buffer + strlen(buffer);
717 *p++ = '/';
718 *p = 0; /* make strlen() below happy */
719 name = strrchrW( newdos_name, '\\' ) + 1;
720 WideCharToMultiByte(DRIVE_GetCodepage(full_name.drive), 0, name, -1,
721 p, sizeof(buffer) - strlen(buffer), NULL, NULL);
722 if ((file = fopen( buffer, "r" )))
724 TRACE("(%s): found it in %s\n", debugstr_w(filename), buffer );
725 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(buffer)+1 );
726 strcpy( CurProfile->unix_name, buffer );
728 else
730 CurProfile->unix_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
731 strcpy( CurProfile->unix_name, full_name.long_name );
732 if ((file = fopen( full_name.long_name, "r" )))
733 TRACE("(%s): found it in %s\n",
734 debugstr_w(filename), full_name.long_name );
737 if (file)
739 CurProfile->section = PROFILE_Load( file );
740 fclose( file );
741 if(!stat(CurProfile->unix_name,&buf))
742 CurProfile->mtime=buf.st_mtime;
744 else
746 /* Does not exist yet, we will create it in PROFILE_FlushFile */
747 WARN("profile file %s not found\n", debugstr_w(newdos_name) );
749 return TRUE;
753 /***********************************************************************
754 * PROFILE_GetSection
756 * Returns all keys of a section.
757 * If return_values is TRUE, also include the corresponding values.
759 static INT PROFILE_GetSection( PROFILESECTION *section, LPCWSTR section_name,
760 LPWSTR buffer, UINT len, BOOL handle_env,
761 BOOL return_values )
763 PROFILEKEY *key;
765 if(!buffer) return 0;
767 TRACE("%s,%p,%u\n", debugstr_w(section_name), buffer, len);
769 while (section)
771 if (section->name[0] && !strcmpiW( section->name, section_name ))
773 UINT oldlen = len;
774 for (key = section->key; key; key = key->next)
776 if (len <= 2) break;
777 if (!*key->name) continue; /* Skip empty lines */
778 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
779 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env, 0 );
780 len -= strlenW(buffer) + 1;
781 buffer += strlenW(buffer) + 1;
782 if (len < 2)
783 break;
784 if (return_values && key->value) {
785 buffer[-1] = '=';
786 PROFILE_CopyEntry ( buffer,
787 key->value, len - 1, handle_env, 0 );
788 len -= strlenW(buffer) + 1;
789 buffer += strlenW(buffer) + 1;
792 *buffer = '\0';
793 if (len <= 1)
794 /*If either lpszSection or lpszKey is NULL and the supplied
795 destination buffer is too small to hold all the strings,
796 the last string is truncated and followed by two null characters.
797 In this case, the return value is equal to cchReturnBuffer
798 minus two. */
800 buffer[-1] = '\0';
801 return oldlen - 2;
803 return oldlen - len;
805 section = section->next;
807 buffer[0] = buffer[1] = '\0';
808 return 0;
811 /* See GetPrivateProfileSectionNamesA for documentation */
812 static INT PROFILE_GetSectionNames( LPWSTR buffer, UINT len )
814 LPWSTR buf;
815 UINT f,l;
816 PROFILESECTION *section;
818 if (!buffer || !len)
819 return 0;
820 if (len==1) {
821 *buffer='\0';
822 return 0;
825 f=len-1;
826 buf=buffer;
827 section = CurProfile->section;
828 while ((section!=NULL)) {
829 if (section->name[0]) {
830 l = strlenW(section->name)+1;
831 if (l > f) {
832 if (f>0) {
833 strncpyW(buf, section->name, f-1);
834 buf += f-1;
835 *buf++='\0';
837 *buf='\0';
838 return len-2;
840 strcpyW(buf, section->name);
841 buf += l;
842 f -= l;
844 section = section->next;
846 *buf='\0';
847 return buf-buffer;
851 /***********************************************************************
852 * PROFILE_GetString
854 * Get a profile string.
856 * Tests with GetPrivateProfileString16, W95a,
857 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
858 * section key_name def_val res buffer
859 * "set1" "1" "x" 43 [data]
860 * "set1" "1 " "x" 43 [data] (!)
861 * "set1" " 1 "' "x" 43 [data] (!)
862 * "set1" "" "x" 1 "x"
863 * "set1" "" "x " 1 "x" (!)
864 * "set1" "" " x " 3 " x" (!)
865 * "set1" NULL "x" 6 "1\02\03\0\0"
866 * "set1" "" "x" 1 "x"
867 * NULL "1" "x" 0 "" (!)
868 * "" "1" "x" 1 "x"
869 * NULL NULL "" 0 ""
873 static INT PROFILE_GetString( LPCWSTR section, LPCWSTR key_name,
874 LPCWSTR def_val, LPWSTR buffer, UINT len )
876 PROFILEKEY *key = NULL;
877 static const WCHAR empty_strW[] = { 0 };
879 if(!buffer) return 0;
881 if (!def_val) def_val = empty_strW;
882 if (key_name)
884 if (!key_name[0])
886 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
887 return 0;
889 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE, FALSE);
890 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
891 len, FALSE, TRUE );
892 TRACE("(%s,%s,%s): returning %s\n",
893 debugstr_w(section), debugstr_w(key_name),
894 debugstr_w(def_val), debugstr_w(buffer) );
895 return strlenW( buffer );
897 /* no "else" here ! */
898 if (section && section[0])
900 INT ret = PROFILE_GetSection(CurProfile->section, section, buffer, len, FALSE, FALSE);
901 if (!buffer[0]) /* no luck -> def_val */
903 PROFILE_CopyEntry(buffer, def_val, len, FALSE, TRUE);
904 ret = strlenW(buffer);
906 return ret;
908 buffer[0] = '\0';
909 return 0;
913 /***********************************************************************
914 * PROFILE_SetString
916 * Set a profile string.
918 static BOOL PROFILE_SetString( LPCWSTR section_name, LPCWSTR key_name,
919 LPCWSTR value, BOOL create_always )
921 if (!key_name) /* Delete a whole section */
923 TRACE("(%s)\n", debugstr_w(section_name));
924 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
925 section_name );
926 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
927 this is not an error on application's level.*/
929 else if (!value) /* Delete a key */
931 TRACE("(%s,%s)\n", debugstr_w(section_name), debugstr_w(key_name) );
932 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
933 section_name, key_name );
934 return TRUE; /* same error handling as above */
936 else /* Set the key value */
938 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
939 key_name, TRUE, create_always );
940 TRACE("(%s,%s,%s):\n",
941 debugstr_w(section_name), debugstr_w(key_name), debugstr_w(value) );
942 if (!key) return FALSE;
943 if (key->value)
945 /* strip the leading spaces. We can safely strip \n\r and
946 * friends too, they should not happen here anyway. */
947 while (PROFILE_isspace(*value)) value++;
949 if (!strcmpW( key->value, value ))
951 TRACE(" no change needed\n" );
952 return TRUE; /* No change needed */
954 TRACE(" replacing %s\n", debugstr_w(key->value) );
955 HeapFree( GetProcessHeap(), 0, key->value );
957 else TRACE(" creating key\n" );
958 key->value = HeapAlloc( GetProcessHeap(), 0, (strlenW(value)+1) * sizeof(WCHAR) );
959 strcpyW( key->value, value );
960 CurProfile->changed = TRUE;
962 return TRUE;
966 /***********************************************************************
967 * PROFILE_GetWineIniString
969 * Get a config string from the wine.ini file.
971 int PROFILE_GetWineIniString( LPCWSTR section, LPCWSTR key_name,
972 LPCWSTR def, LPWSTR buffer, int len )
974 HKEY hkey;
975 NTSTATUS err;
976 OBJECT_ATTRIBUTES attr;
977 UNICODE_STRING nameW;
979 attr.Length = sizeof(attr);
980 attr.RootDirectory = wine_profile_key;
981 attr.ObjectName = &nameW;
982 attr.Attributes = 0;
983 attr.SecurityDescriptor = NULL;
984 attr.SecurityQualityOfService = NULL;
985 RtlInitUnicodeString( &nameW, section );
986 if (!(err = NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )))
988 char tmp[PROFILE_MAX_LINE_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
989 DWORD count;
991 RtlInitUnicodeString( &nameW, key_name );
992 if (!(err = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
993 tmp, sizeof(tmp), &count )))
995 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
996 PROFILE_CopyEntry( buffer, str, len, TRUE, TRUE );
998 NtClose( hkey );
1001 if (err) PROFILE_CopyEntry( buffer, def, len, TRUE, TRUE );
1002 TRACE( "(%s,%s,%s): returning %s\n", debugstr_w(section),
1003 debugstr_w(key_name), debugstr_w(def), debugstr_w(buffer) );
1004 return strlenW(buffer);
1008 /******************************************************************************
1010 * PROFILE_GetWineIniBool
1012 * Reads a boolean value from the wine.ini file. This function attempts to
1013 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
1014 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
1015 * true. Anything else results in the return of the default value.
1017 * This function uses 1 to indicate true, and 0 for false. You can check
1018 * for existence by setting def to something other than 0 or 1 and
1019 * examining the return value.
1021 int PROFILE_GetWineIniBool( LPCWSTR section, LPCWSTR key_name, int def )
1023 static const WCHAR def_valueW[] = {'~',0};
1024 WCHAR key_value[2];
1025 int retval;
1027 PROFILE_GetWineIniString(section, key_name, def_valueW, key_value, 2);
1029 switch(key_value[0]) {
1030 case 'n':
1031 case 'N':
1032 case 'f':
1033 case 'F':
1034 case '0':
1035 retval = 0;
1036 break;
1038 case 'y':
1039 case 'Y':
1040 case 't':
1041 case 'T':
1042 case '1':
1043 retval = 1;
1044 break;
1046 default:
1047 retval = def;
1050 TRACE("(%s, %s, %s), [%c], ret %s\n", debugstr_w(section), debugstr_w(key_name),
1051 def ? "TRUE" : "FALSE", key_value[0],
1052 retval ? "TRUE" : "FALSE");
1054 return retval;
1058 /***********************************************************************
1059 * PROFILE_LoadWineIni
1061 * Load the old .winerc file.
1063 int PROFILE_LoadWineIni(void)
1065 OBJECT_ATTRIBUTES attr;
1066 UNICODE_STRING nameW;
1067 char buffer[MAX_PATHNAME_LEN];
1068 const char *p;
1069 FILE *f;
1070 HKEY hKeySW;
1071 DWORD disp;
1073 attr.Length = sizeof(attr);
1074 attr.RootDirectory = 0;
1075 attr.ObjectName = &nameW;
1076 attr.Attributes = 0;
1077 attr.SecurityDescriptor = NULL;
1078 attr.SecurityQualityOfService = NULL;
1080 /* make sure HKLM\\Software\\Wine\\Wine exists as non-volatile key */
1081 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine" ) ||
1082 NtCreateKey( &hKeySW, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp ))
1084 ERR("Cannot create config registry key\n" );
1085 ExitProcess( 1 );
1087 RtlFreeUnicodeString( &nameW );
1088 NtClose( hKeySW );
1090 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config" ) ||
1091 NtCreateKey( &wine_profile_key, KEY_ALL_ACCESS, &attr, 0,
1092 NULL, REG_OPTION_VOLATILE, &disp ))
1094 ERR("Cannot create config registry key\n" );
1095 ExitProcess( 1 );
1097 RtlFreeUnicodeString( &nameW );
1099 if (disp == REG_OPENED_EXISTING_KEY) return 1; /* loaded by the server */
1101 if ((p = getenv( "HOME" )) != NULL)
1103 lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
1104 strcat( buffer, PROFILE_WineIniName );
1105 if ((f = fopen( buffer, "r" )) != NULL)
1107 lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
1109 /* convert to the new format */
1110 sprintf( buffer, "%s/config", wine_get_config_dir() );
1111 convert_config( f, buffer );
1112 fclose( f );
1114 MESSAGE( "The '%s' configuration file has been converted\n"
1115 "to the new format and saved as '%s'.\n", PROFILE_WineIniUsed, buffer );
1116 MESSAGE( "You should verify that the contents of the new file are correct,\n"
1117 "and then remove the old one and restart Wine.\n" );
1118 ExitProcess(0);
1121 else WARN("could not get $HOME value for config file.\n" );
1123 MESSAGE( "Can't open configuration file %s/config\n", wine_get_config_dir() );
1124 return 0;
1128 /***********************************************************************
1129 * PROFILE_UsageWineIni
1131 * Explain the wine.ini file to those who don't read documentation.
1132 * Keep below one screenful in length so that error messages above are
1133 * noticed.
1135 void PROFILE_UsageWineIni(void)
1137 MESSAGE("Perhaps you have not properly edited or created "
1138 "your Wine configuration file.\n");
1139 MESSAGE("This is (supposed to be) '%s/config'\n", wine_get_config_dir());
1140 /* RTFM, so to say */
1144 /********************* API functions **********************************/
1146 /***********************************************************************
1147 * GetProfileInt (KERNEL.57)
1149 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1151 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1155 /***********************************************************************
1156 * GetProfileIntA (KERNEL32.@)
1158 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1160 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1163 /***********************************************************************
1164 * GetProfileIntW (KERNEL32.@)
1166 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1168 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1172 * if allow_section_name_copy is TRUE, allow the copying :
1173 * - of Section names if 'section' is NULL
1174 * - of Keys in a Section if 'entry' is NULL
1175 * (see MSDN doc for GetPrivateProfileString)
1177 static int PROFILE_GetPrivateProfileString( LPCWSTR section, LPCWSTR entry,
1178 LPCWSTR def_val, LPWSTR buffer,
1179 UINT len, LPCWSTR filename,
1180 BOOL allow_section_name_copy )
1182 int ret;
1183 LPWSTR pDefVal = NULL;
1185 if (!filename)
1186 filename = wininiW;
1188 TRACE("%s,%s,%s,%p,%u,%s\n", debugstr_w(section), debugstr_w(entry),
1189 debugstr_w(def_val), buffer, len, debugstr_w(filename));
1191 /* strip any trailing ' ' of def_val. */
1192 if (def_val)
1194 LPCWSTR p = &def_val[strlenW(def_val)]; /* even "" works ! */
1196 while (p > def_val)
1198 p--;
1199 if ((*p) != ' ')
1200 break;
1202 if (*p == ' ') /* ouch, contained trailing ' ' */
1204 int len = (int)(p - def_val);
1205 pDefVal = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1206 strncpyW(pDefVal, def_val, len);
1207 pDefVal[len] = '\0';
1210 if (!pDefVal)
1211 pDefVal = (LPWSTR)def_val;
1213 EnterCriticalSection( &PROFILE_CritSect );
1215 if (PROFILE_Open( filename )) {
1216 if ((allow_section_name_copy) && (section == NULL))
1217 ret = PROFILE_GetSectionNames(buffer, len);
1218 else
1219 /* PROFILE_GetString already handles the 'entry == NULL' case */
1220 ret = PROFILE_GetString( section, entry, pDefVal, buffer, len );
1221 } else {
1222 lstrcpynW( buffer, pDefVal, len );
1223 ret = strlenW( buffer );
1226 LeaveCriticalSection( &PROFILE_CritSect );
1228 if (pDefVal != def_val) /* allocated */
1229 HeapFree(GetProcessHeap(), 0, pDefVal);
1231 TRACE("returning %s, %d\n", debugstr_w(buffer), ret);
1233 return ret;
1236 /***********************************************************************
1237 * GetPrivateProfileString (KERNEL.128)
1239 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1240 LPCSTR def_val, LPSTR buffer,
1241 UINT16 len, LPCSTR filename )
1243 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1244 LPWSTR bufferW;
1245 INT16 retW, ret = 0;
1247 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1248 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1249 else sectionW.Buffer = NULL;
1250 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1251 else entryW.Buffer = NULL;
1252 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1253 else def_valW.Buffer = NULL;
1254 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1255 else filenameW.Buffer = NULL;
1257 retW = PROFILE_GetPrivateProfileString( sectionW.Buffer, entryW.Buffer,
1258 def_valW.Buffer, bufferW, len,
1259 filenameW.Buffer, FALSE );
1260 if (len)
1262 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1263 if (!ret)
1265 ret = len - 1;
1266 buffer[ret] = 0;
1268 else
1269 ret--; /* strip terminating 0 */
1272 RtlFreeUnicodeString(&sectionW);
1273 RtlFreeUnicodeString(&entryW);
1274 RtlFreeUnicodeString(&def_valW);
1275 RtlFreeUnicodeString(&filenameW);
1276 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1277 return ret;
1280 /***********************************************************************
1281 * GetPrivateProfileStringA (KERNEL32.@)
1283 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1284 LPCSTR def_val, LPSTR buffer,
1285 UINT len, LPCSTR filename )
1287 UNICODE_STRING sectionW, entryW, def_valW, filenameW;
1288 LPWSTR bufferW;
1289 INT retW, ret = 0;
1291 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1292 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1293 else sectionW.Buffer = NULL;
1294 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1295 else entryW.Buffer = NULL;
1296 if (def_val) RtlCreateUnicodeStringFromAsciiz(&def_valW, def_val);
1297 else def_valW.Buffer = NULL;
1298 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1299 else filenameW.Buffer = NULL;
1301 retW = GetPrivateProfileStringW( sectionW.Buffer, entryW.Buffer,
1302 def_valW.Buffer, bufferW, len,
1303 filenameW.Buffer);
1304 if (len)
1306 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 1, buffer, len, NULL, NULL);
1307 if (!ret)
1309 ret = len - 1;
1310 buffer[ret] = 0;
1312 else
1313 ret--; /* strip terminating 0 */
1316 RtlFreeUnicodeString(&sectionW);
1317 RtlFreeUnicodeString(&entryW);
1318 RtlFreeUnicodeString(&def_valW);
1319 RtlFreeUnicodeString(&filenameW);
1320 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1321 return ret;
1324 /***********************************************************************
1325 * GetPrivateProfileStringW (KERNEL32.@)
1327 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1328 LPCWSTR def_val, LPWSTR buffer,
1329 UINT len, LPCWSTR filename )
1331 return PROFILE_GetPrivateProfileString( section, entry, def_val,
1332 buffer, len, filename, TRUE );
1335 /***********************************************************************
1336 * GetProfileString (KERNEL.58)
1338 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1339 LPSTR buffer, UINT16 len )
1341 return GetPrivateProfileString16( section, entry, def_val,
1342 buffer, len, "win.ini" );
1345 /***********************************************************************
1346 * GetProfileStringA (KERNEL32.@)
1348 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1349 LPSTR buffer, UINT len )
1351 return GetPrivateProfileStringA( section, entry, def_val,
1352 buffer, len, "win.ini" );
1355 /***********************************************************************
1356 * GetProfileStringW (KERNEL32.@)
1358 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1359 LPCWSTR def_val, LPWSTR buffer, UINT len )
1361 return GetPrivateProfileStringW( section, entry, def_val,
1362 buffer, len, wininiW );
1365 /***********************************************************************
1366 * WriteProfileString (KERNEL.59)
1368 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1369 LPCSTR string )
1371 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1374 /***********************************************************************
1375 * WriteProfileStringA (KERNEL32.@)
1377 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1378 LPCSTR string )
1380 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1383 /***********************************************************************
1384 * WriteProfileStringW (KERNEL32.@)
1386 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1387 LPCWSTR string )
1389 return WritePrivateProfileStringW( section, entry, string, wininiW );
1393 /***********************************************************************
1394 * GetPrivateProfileInt (KERNEL.127)
1396 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1397 INT16 def_val, LPCSTR filename )
1399 /* we used to have some elaborate return value limitation (<= -32768 etc.)
1400 * here, but Win98SE doesn't care about this at all, so I deleted it.
1401 * AFAIR versions prior to Win9x had these limits, though. */
1402 return (INT16)GetPrivateProfileIntA(section,entry,def_val,filename);
1405 /***********************************************************************
1406 * GetPrivateProfileIntA (KERNEL32.@)
1408 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1409 INT def_val, LPCSTR filename )
1411 char buffer[20];
1412 long result;
1414 if (!GetPrivateProfileStringA( section, entry, "",
1415 buffer, sizeof(buffer), filename ))
1416 return def_val;
1417 /* FIXME: if entry can be found but it's empty, then Win16 is
1418 * supposed to return 0 instead of def_val ! Difficult/problematic
1419 * to implement (every other failure also returns zero buffer),
1420 * thus wait until testing framework avail for making sure nothing
1421 * else gets broken that way. */
1422 if (!buffer[0]) return (UINT)def_val;
1424 /* Don't use strtol() here !
1425 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1426 YES, scan for unsigned format ! (otherwise compatibility error) */
1427 if (!sscanf(buffer, "%lu", &result)) return 0;
1428 return (UINT)result;
1431 /***********************************************************************
1432 * GetPrivateProfileIntW (KERNEL32.@)
1434 * FIXME: rewrite using unicode
1436 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1437 INT def_val, LPCWSTR filename )
1439 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1440 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1441 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1442 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1443 HeapFree( GetProcessHeap(), 0, sectionA );
1444 HeapFree( GetProcessHeap(), 0, filenameA );
1445 HeapFree( GetProcessHeap(), 0, entryA );
1446 return res;
1449 /***********************************************************************
1450 * GetPrivateProfileSection (KERNEL.418)
1452 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1453 UINT16 len, LPCSTR filename )
1455 return GetPrivateProfileSectionA( section, buffer, len, filename );
1458 /***********************************************************************
1459 * GetPrivateProfileSectionW (KERNEL32.@)
1461 INT WINAPI GetPrivateProfileSectionW( LPCWSTR section, LPWSTR buffer,
1462 DWORD len, LPCWSTR filename )
1464 int ret = 0;
1466 EnterCriticalSection( &PROFILE_CritSect );
1468 if (PROFILE_Open( filename ))
1469 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1470 FALSE, TRUE);
1472 LeaveCriticalSection( &PROFILE_CritSect );
1474 return ret;
1477 /***********************************************************************
1478 * GetPrivateProfileSectionA (KERNEL32.@)
1480 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1481 DWORD len, LPCSTR filename )
1483 UNICODE_STRING sectionW, filenameW;
1484 LPWSTR bufferW;
1485 INT retW, ret = 0;
1487 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)) : NULL;
1488 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1489 else sectionW.Buffer = NULL;
1490 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1491 else filenameW.Buffer = NULL;
1493 retW = GetPrivateProfileSectionW(sectionW.Buffer, bufferW, len, filenameW.Buffer);
1494 if (len > 2)
1496 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW + 2, buffer, len, NULL, NULL);
1497 if (ret > 2)
1498 ret -= 2;
1499 else
1501 ret = 0;
1502 buffer[len-2] = 0;
1503 buffer[len-1] = 0;
1506 else
1508 buffer[0] = 0;
1509 buffer[1] = 0;
1512 RtlFreeUnicodeString(&sectionW);
1513 RtlFreeUnicodeString(&filenameW);
1514 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1515 return ret;
1518 /***********************************************************************
1519 * GetProfileSection (KERNEL.419)
1521 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1523 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1526 /***********************************************************************
1527 * GetProfileSectionA (KERNEL32.@)
1529 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1531 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1534 /***********************************************************************
1535 * GetProfileSectionW (KERNEL32.@)
1537 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1539 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1543 /***********************************************************************
1544 * WritePrivateProfileString (KERNEL.129)
1546 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1547 LPCSTR string, LPCSTR filename )
1549 return WritePrivateProfileStringA(section,entry,string,filename);
1552 /***********************************************************************
1553 * WritePrivateProfileStringW (KERNEL32.@)
1555 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1556 LPCWSTR string, LPCWSTR filename )
1558 BOOL ret = FALSE;
1560 EnterCriticalSection( &PROFILE_CritSect );
1562 if (PROFILE_Open( filename ))
1564 if (!section && !entry && !string) /* documented "file flush" case */
1566 PROFILE_FlushFile();
1567 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1569 else {
1570 if (!section) {
1571 FIXME("(NULL?,%s,%s,%s)?\n",
1572 debugstr_w(entry), debugstr_w(string), debugstr_w(filename));
1573 } else {
1574 ret = PROFILE_SetString( section, entry, string, FALSE);
1575 PROFILE_FlushFile();
1580 LeaveCriticalSection( &PROFILE_CritSect );
1581 return ret;
1584 /***********************************************************************
1585 * WritePrivateProfileStringA (KERNEL32.@)
1587 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1588 LPCSTR string, LPCSTR filename )
1590 UNICODE_STRING sectionW, entryW, stringW, filenameW;
1591 BOOL ret;
1593 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1594 else sectionW.Buffer = NULL;
1595 if (entry) RtlCreateUnicodeStringFromAsciiz(&entryW, entry);
1596 else entryW.Buffer = NULL;
1597 if (string) RtlCreateUnicodeStringFromAsciiz(&stringW, string);
1598 else stringW.Buffer = NULL;
1599 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1600 else filenameW.Buffer = NULL;
1602 ret = WritePrivateProfileStringW(sectionW.Buffer, entryW.Buffer,
1603 stringW.Buffer, filenameW.Buffer);
1604 RtlFreeUnicodeString(&sectionW);
1605 RtlFreeUnicodeString(&entryW);
1606 RtlFreeUnicodeString(&stringW);
1607 RtlFreeUnicodeString(&filenameW);
1608 return ret;
1611 /***********************************************************************
1612 * WritePrivateProfileSection (KERNEL.416)
1614 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1615 LPCSTR string, LPCSTR filename )
1617 return WritePrivateProfileSectionA( section, string, filename );
1620 /***********************************************************************
1621 * WritePrivateProfileSectionW (KERNEL32.@)
1623 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1624 LPCWSTR string, LPCWSTR filename )
1626 BOOL ret = FALSE;
1627 LPWSTR p;
1629 EnterCriticalSection( &PROFILE_CritSect );
1631 if (PROFILE_Open( filename )) {
1632 if (!section && !string)
1633 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1634 else if (!string) {/* delete the named section*/
1635 ret = PROFILE_SetString(section,NULL,NULL, FALSE);
1636 PROFILE_FlushFile();
1637 } else {
1638 PROFILE_DeleteAllKeys(section);
1639 ret = TRUE;
1640 while(*string) {
1641 LPWSTR buf = HeapAlloc( GetProcessHeap(), 0, (strlenW(string)+1) * sizeof(WCHAR) );
1642 strcpyW( buf, string );
1643 if((p = strchrW( buf, '='))) {
1644 *p='\0';
1645 ret = PROFILE_SetString( section, buf, p+1, TRUE);
1647 HeapFree( GetProcessHeap(), 0, buf );
1648 string += strlenW(string)+1;
1650 PROFILE_FlushFile();
1654 LeaveCriticalSection( &PROFILE_CritSect );
1655 return ret;
1658 /***********************************************************************
1659 * WritePrivateProfileSectionA (KERNEL32.@)
1661 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1662 LPCSTR string, LPCSTR filename)
1665 UNICODE_STRING sectionW, filenameW;
1666 LPWSTR stringW;
1667 BOOL ret;
1669 if (string)
1671 INT lenA, lenW;
1672 LPCSTR p = string;
1674 while(*p) p += strlen(p) + 1;
1675 lenA = p - string + 1;
1676 lenW = MultiByteToWideChar(CP_ACP, 0, string, lenA, NULL, 0);
1677 if ((stringW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR))))
1678 MultiByteToWideChar(CP_ACP, 0, string, lenA, stringW, lenW);
1680 else stringW = NULL;
1681 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1682 else sectionW.Buffer = NULL;
1683 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1684 else filenameW.Buffer = NULL;
1686 ret = WritePrivateProfileSectionW(sectionW.Buffer, stringW, filenameW.Buffer);
1688 HeapFree(GetProcessHeap(), 0, stringW);
1689 RtlFreeUnicodeString(&sectionW);
1690 RtlFreeUnicodeString(&filenameW);
1691 return ret;
1694 /***********************************************************************
1695 * WriteProfileSection (KERNEL.417)
1697 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1699 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1702 /***********************************************************************
1703 * WriteProfileSectionA (KERNEL32.@)
1705 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1708 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1711 /***********************************************************************
1712 * WriteProfileSectionW (KERNEL32.@)
1714 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1716 return WritePrivateProfileSectionW(section, keys_n_values, wininiW);
1719 /***********************************************************************
1720 * GetPrivateProfileSectionNames (KERNEL.143)
1722 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1723 LPCSTR filename )
1725 return GetPrivateProfileSectionNamesA(buffer,size,filename);
1729 /***********************************************************************
1730 * GetProfileSectionNames (KERNEL.142)
1732 WORD WINAPI GetProfileSectionNames16(LPSTR buffer, WORD size)
1735 return GetPrivateProfileSectionNamesA(buffer,size,"win.ini");
1739 /***********************************************************************
1740 * GetPrivateProfileSectionNamesW (KERNEL32.@)
1742 * Returns the section names contained in the specified file.
1743 * FIXME: Where do we find this file when the path is relative?
1744 * The section names are returned as a list of strings with an extra
1745 * '\0' to mark the end of the list. Except for that the behavior
1746 * depends on the Windows version.
1748 * Win95:
1749 * - if the buffer is 0 or 1 character long then it is as if it was of
1750 * infinite length.
1751 * - otherwise, if the buffer is to small only the section names that fit
1752 * are returned.
1753 * - note that this means if the buffer was to small to return even just
1754 * the first section name then a single '\0' will be returned.
1755 * - the return value is the number of characters written in the buffer,
1756 * except if the buffer was too smal in which case len-2 is returned
1758 * Win2000:
1759 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1760 * '\0' and the return value is 0
1761 * - otherwise if the buffer is too small then the first section name that
1762 * does not fit is truncated so that the string list can be terminated
1763 * correctly (double '\0')
1764 * - the return value is the number of characters written in the buffer
1765 * except for the trailing '\0'. If the buffer is too small, then the
1766 * return value is len-2
1767 * - Win2000 has a bug that triggers when the section names and the
1768 * trailing '\0' fit exactly in the buffer. In that case the trailing
1769 * '\0' is missing.
1771 * Wine implements the observed Win2000 behavior (except for the bug).
1773 * Note that when the buffer is big enough then the return value may be any
1774 * value between 1 and len-1 (or len in Win95), including len-2.
1776 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1777 LPCWSTR filename)
1779 DWORD ret = 0;
1781 EnterCriticalSection( &PROFILE_CritSect );
1783 if (PROFILE_Open( filename ))
1784 ret = PROFILE_GetSectionNames(buffer, size);
1786 LeaveCriticalSection( &PROFILE_CritSect );
1788 return ret;
1792 /***********************************************************************
1793 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1795 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1796 LPCSTR filename)
1798 UNICODE_STRING filenameW;
1799 LPWSTR bufferW;
1800 INT retW, ret = 0;
1802 bufferW = buffer ? HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)) : NULL;
1803 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1804 else filenameW.Buffer = NULL;
1806 retW = GetPrivateProfileSectionNamesW(bufferW, size, filenameW.Buffer);
1807 if (retW && size)
1809 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, retW, buffer, size, NULL, NULL);
1810 if (!ret)
1812 ret = size;
1813 buffer[size-1] = 0;
1817 RtlFreeUnicodeString(&filenameW);
1818 if (bufferW) HeapFree(GetProcessHeap(), 0, bufferW);
1819 return ret;
1822 /***********************************************************************
1823 * GetPrivateProfileStruct (KERNEL.407)
1825 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1826 LPVOID buf, UINT16 len, LPCSTR filename)
1828 return GetPrivateProfileStructA( section, key, buf, len, filename );
1831 /***********************************************************************
1832 * GetPrivateProfileStructW (KERNEL32.@)
1834 * Should match Win95's behaviour pretty much
1836 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1837 LPVOID buf, UINT len, LPCWSTR filename)
1839 BOOL ret = FALSE;
1841 EnterCriticalSection( &PROFILE_CritSect );
1843 if (PROFILE_Open( filename )) {
1844 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE, FALSE);
1845 if (k) {
1846 TRACE("value (at %p): %s\n", k->value, debugstr_w(k->value));
1847 if (((strlenW(k->value) - 2) / 2) == len)
1849 LPWSTR end, p;
1850 BOOL valid = TRUE;
1851 WCHAR c;
1852 DWORD chksum = 0;
1854 end = k->value + strlenW(k->value); /* -> '\0' */
1855 /* check for invalid chars in ASCII coded hex string */
1856 for (p=k->value; p < end; p++)
1858 if (!isxdigitW(*p))
1860 WARN("invalid char '%x' in file %s->[%s]->%s !\n",
1861 *p, debugstr_w(filename), debugstr_w(section), debugstr_w(key));
1862 valid = FALSE;
1863 break;
1866 if (valid)
1868 BOOL highnibble = TRUE;
1869 BYTE b = 0, val;
1870 LPBYTE binbuf = (LPBYTE)buf;
1872 end -= 2; /* don't include checksum in output data */
1873 /* translate ASCII hex format into binary data */
1874 for (p=k->value; p < end; p++)
1876 c = toupperW(*p);
1877 val = (c > '9') ?
1878 (c - 'A' + 10) : (c - '0');
1880 if (highnibble)
1881 b = val << 4;
1882 else
1884 b += val;
1885 *binbuf++ = b; /* feed binary data into output */
1886 chksum += b; /* calculate checksum */
1888 highnibble ^= 1; /* toggle */
1890 /* retrieve stored checksum value */
1891 c = toupperW(*p++);
1892 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1893 c = toupperW(*p);
1894 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1895 if (b == (chksum & 0xff)) /* checksums match ? */
1896 ret = TRUE;
1901 LeaveCriticalSection( &PROFILE_CritSect );
1903 return ret;
1906 /***********************************************************************
1907 * GetPrivateProfileStructA (KERNEL32.@)
1909 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1910 LPVOID buffer, UINT len, LPCSTR filename)
1912 UNICODE_STRING sectionW, keyW, filenameW;
1913 INT ret;
1915 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1916 else sectionW.Buffer = NULL;
1917 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1918 else keyW.Buffer = NULL;
1919 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1920 else filenameW.Buffer = NULL;
1922 ret = GetPrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buffer, len,
1923 filenameW.Buffer);
1924 /* Do not translate binary data. */
1926 RtlFreeUnicodeString(&sectionW);
1927 RtlFreeUnicodeString(&keyW);
1928 RtlFreeUnicodeString(&filenameW);
1929 return ret;
1934 /***********************************************************************
1935 * WritePrivateProfileStruct (KERNEL.406)
1937 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1938 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1940 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1943 /***********************************************************************
1944 * WritePrivateProfileStructW (KERNEL32.@)
1946 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1947 LPVOID buf, UINT bufsize, LPCWSTR filename)
1949 BOOL ret = FALSE;
1950 LPBYTE binbuf;
1951 LPWSTR outstring, p;
1952 DWORD sum = 0;
1954 if (!section && !key && !buf) /* flush the cache */
1955 return WritePrivateProfileStringW( NULL, NULL, NULL, filename );
1957 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1958 outstring = HeapAlloc( GetProcessHeap(), 0, (bufsize*2 + 2 + 1) * sizeof(WCHAR) );
1959 p = outstring;
1960 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1961 *p++ = hex[*binbuf >> 4];
1962 *p++ = hex[*binbuf & 0xf];
1963 sum += *binbuf;
1965 /* checksum is sum & 0xff */
1966 *p++ = hex[(sum & 0xf0) >> 4];
1967 *p++ = hex[sum & 0xf];
1968 *p++ = '\0';
1970 EnterCriticalSection( &PROFILE_CritSect );
1972 if (PROFILE_Open( filename )) {
1973 ret = PROFILE_SetString( section, key, outstring, FALSE);
1974 PROFILE_FlushFile();
1977 LeaveCriticalSection( &PROFILE_CritSect );
1979 HeapFree( GetProcessHeap(), 0, outstring );
1981 return ret;
1984 /***********************************************************************
1985 * WritePrivateProfileStructA (KERNEL32.@)
1987 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1988 LPVOID buf, UINT bufsize, LPCSTR filename)
1990 UNICODE_STRING sectionW, keyW, filenameW;
1991 INT ret;
1993 if (section) RtlCreateUnicodeStringFromAsciiz(&sectionW, section);
1994 else sectionW.Buffer = NULL;
1995 if (key) RtlCreateUnicodeStringFromAsciiz(&keyW, key);
1996 else keyW.Buffer = NULL;
1997 if (filename) RtlCreateUnicodeStringFromAsciiz(&filenameW, filename);
1998 else filenameW.Buffer = NULL;
2000 /* Do not translate binary data. */
2001 ret = WritePrivateProfileStructW(sectionW.Buffer, keyW.Buffer, buf, bufsize,
2002 filenameW.Buffer);
2004 RtlFreeUnicodeString(&sectionW);
2005 RtlFreeUnicodeString(&keyW);
2006 RtlFreeUnicodeString(&filenameW);
2007 return ret;
2011 /***********************************************************************
2012 * WriteOutProfiles (KERNEL.315)
2014 void WINAPI WriteOutProfiles16(void)
2016 EnterCriticalSection( &PROFILE_CritSect );
2017 PROFILE_FlushFile();
2018 LeaveCriticalSection( &PROFILE_CritSect );
2021 /***********************************************************************
2022 * CloseProfileUserMapping (KERNEL32.@)
2024 BOOL WINAPI CloseProfileUserMapping(void) {
2025 FIXME("(), stub!\n");
2026 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2027 return FALSE;