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