Added support for WINEPREFIX environment variable.
[wine.git] / files / profile.c
blobf578c90e3684be3fdc1ec078f09c839ee419ea34
1 /*
2 * Profile functions
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include <ctype.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <pwd.h>
15 #include <unistd.h>
17 #include "winbase.h"
18 #include "winerror.h"
19 #include "wine/winbase16.h"
20 #include "windef.h"
21 #include "wingdi.h"
22 #include "winuser.h"
23 #include "winnls.h"
24 #include "winreg.h"
25 #include "file.h"
26 #include "heap.h"
27 #include "debugtools.h"
28 #include "options.h"
29 #include "server.h"
31 DEFAULT_DEBUG_CHANNEL(profile);
33 typedef struct tagPROFILEKEY
35 char *name;
36 char *value;
37 struct tagPROFILEKEY *next;
38 } PROFILEKEY;
40 typedef struct tagPROFILESECTION
42 char *name;
43 struct tagPROFILEKEY *key;
44 struct tagPROFILESECTION *next;
45 } PROFILESECTION;
48 typedef struct
50 BOOL changed;
51 PROFILESECTION *section;
52 char *dos_name;
53 char *unix_name;
54 char *filename;
55 time_t mtime;
56 } PROFILE;
59 #define N_CACHED_PROFILES 10
61 /* Cached profile files */
62 static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
64 #define CurProfile (MRUProfile[0])
66 /* wine.ini config file registry root */
67 static HKEY wine_profile_key;
69 #define PROFILE_MAX_LINE_LEN 1024
71 /* Wine profile name in $HOME directory; must begin with slash */
72 static const char PROFILE_WineIniName[] = "/.winerc";
74 /* Wine profile: the profile file being used */
75 static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
77 /* Check for comments in profile */
78 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
80 #define WINE_INI_GLOBAL ETCDIR "/wine.conf"
82 static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
84 static CRITICAL_SECTION PROFILE_CritSect;
86 static const char hex[16] = "0123456789ABCDEF";
88 /***********************************************************************
89 * PROFILE_CopyEntry
91 * Copy the content of an entry into a buffer, removing quotes, and possibly
92 * translating environment variables.
94 static void PROFILE_CopyEntry( char *buffer, const char *value, int len,
95 int handle_env )
97 char quote = '\0';
98 const char *p;
100 if ((*value == '\'') || (*value == '\"'))
102 if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
105 if (!handle_env)
107 lstrcpynA( buffer, value, len );
108 if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
109 return;
112 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
114 if ((*p == '$') && (p[1] == '{'))
116 char env_val[1024];
117 const char *env_p;
118 const char *p2 = strchr( p, '}' );
119 if (!p2) continue; /* ignore it */
120 lstrcpynA(env_val, p + 2, min( sizeof(env_val), (int)(p2-p)-1 ));
121 if ((env_p = getenv( env_val )) != NULL)
123 lstrcpynA( buffer, env_p, len );
124 buffer += strlen( buffer );
125 len -= strlen( buffer );
127 p = p2 + 1;
130 if (quote && (len > 1)) buffer--;
131 *buffer = '\0';
135 /***********************************************************************
136 * PROFILE_Save
138 * Save a profile tree to a file.
140 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
142 PROFILEKEY *key;
144 for ( ; section; section = section->next)
146 if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name );
147 for (key = section->key; key; key = key->next)
149 fprintf( file, "%s", key->name );
150 if (key->value) fprintf( file, "=%s", key->value );
151 fprintf( file, "\r\n" );
157 /***********************************************************************
158 * PROFILE_Free
160 * Free a profile tree.
162 static void PROFILE_Free( PROFILESECTION *section )
164 PROFILESECTION *next_section;
165 PROFILEKEY *key, *next_key;
167 for ( ; section; section = next_section)
169 if (section->name) HeapFree( GetProcessHeap(), 0, section->name );
170 for (key = section->key; key; key = next_key)
172 next_key = key->next;
173 if (key->name) HeapFree( GetProcessHeap(), 0, key->name );
174 if (key->value) HeapFree( GetProcessHeap(), 0, key->value );
175 HeapFree( GetProcessHeap(), 0, key );
177 next_section = section->next;
178 HeapFree( GetProcessHeap(), 0, section );
182 static inline int PROFILE_isspace(char c)
184 if (isspace(c)) return 1;
185 if (c=='\r' || c==0x1a) return 1;
186 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
187 return 0;
191 /***********************************************************************
192 * PROFILE_Load
194 * Load a profile tree from a file.
196 static PROFILESECTION *PROFILE_Load( FILE *file )
198 char buffer[PROFILE_MAX_LINE_LEN];
199 char *p, *p2;
200 int line = 0;
201 PROFILESECTION *section, *first_section;
202 PROFILESECTION **next_section;
203 PROFILEKEY *key, *prev_key, **next_key;
205 first_section = HEAP_xalloc( GetProcessHeap(), 0, sizeof(*section) );
206 first_section->name = NULL;
207 first_section->key = NULL;
208 first_section->next = NULL;
209 next_section = &first_section->next;
210 next_key = &first_section->key;
211 prev_key = NULL;
213 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
215 line++;
216 p = buffer;
217 while (*p && PROFILE_isspace(*p)) p++;
218 if (*p == '[') /* section start */
220 if (!(p2 = strrchr( p, ']' )))
222 WARN("Invalid section header at line %d: '%s'\n",
223 line, p );
225 else
227 *p2 = '\0';
228 p++;
229 section = HEAP_xalloc( GetProcessHeap(), 0, sizeof(*section) );
230 section->name = HEAP_strdupA( GetProcessHeap(), 0, p );
231 section->key = NULL;
232 section->next = NULL;
233 *next_section = section;
234 next_section = &section->next;
235 next_key = &section->key;
236 prev_key = NULL;
238 TRACE("New section: '%s'\n",section->name);
240 continue;
244 p2=p+strlen(p) - 1;
245 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
247 if ((p2 = strchr( p, '=' )) != NULL)
249 char *p3 = p2 - 1;
250 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
251 *p2++ = '\0';
252 while (*p2 && PROFILE_isspace(*p2)) p2++;
255 if(*p || !prev_key || *prev_key->name)
257 key = HEAP_xalloc( GetProcessHeap(), 0, sizeof(*key) );
258 key->name = HEAP_strdupA( GetProcessHeap(), 0, p );
259 key->value = p2 ? HEAP_strdupA( GetProcessHeap(), 0, p2 ) : NULL;
260 key->next = NULL;
261 *next_key = key;
262 next_key = &key->next;
263 prev_key = key;
265 TRACE("New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)");
268 return first_section;
272 /***********************************************************************
273 * PROFILE_RegistryLoad
275 * Load a profile tree from a file into a registry key.
277 static DWORD PROFILE_RegistryLoad( HKEY root, FILE *file )
279 HKEY hkey = 0;
280 DWORD err = 0;
281 char buffer[PROFILE_MAX_LINE_LEN];
282 char *p, *p2;
283 int line = 0;
285 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
287 line++;
288 p = buffer;
289 while (*p && PROFILE_isspace(*p)) p++;
290 if (*p == '[') /* section start */
292 if (!(p2 = strrchr( p, ']' )))
294 WARN("Invalid section header at line %d: '%s'\n",
295 line, p );
297 else
299 *p2 = '\0';
300 p++;
301 if (hkey) RegCloseKey( hkey );
302 if ((err = RegCreateKeyExA( root, p, 0, NULL, REG_OPTION_VOLATILE,
303 KEY_ALL_ACCESS, NULL, &hkey, NULL ))) return err;
304 TRACE("New section: '%s'\n",p);
305 continue;
309 p2=p+strlen(p) - 1;
310 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
312 if ((p2 = strchr( p, '=' )) != NULL)
314 char *p3 = p2 - 1;
315 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
316 *p2++ = '\0';
317 while (*p2 && PROFILE_isspace(*p2)) p2++;
320 if (*p && hkey && !IS_ENTRY_COMMENT(p))
322 if (!p2) p2 = "";
323 if ((err = RegSetValueExA( hkey, p, 0, REG_SZ, p2, strlen(p2)+1 )))
325 RegCloseKey( hkey );
326 return err;
328 TRACE("New key: name='%s', value='%s'\n",p,p2);
331 if (hkey) RegCloseKey( hkey );
332 return 0;
336 /***********************************************************************
337 * PROFILE_DeleteSection
339 * Delete a section from a profile tree.
341 static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
343 while (*section)
345 if ((*section)->name && !strcasecmp( (*section)->name, name ))
347 PROFILESECTION *to_del = *section;
348 *section = to_del->next;
349 to_del->next = NULL;
350 PROFILE_Free( to_del );
351 return TRUE;
353 section = &(*section)->next;
355 return FALSE;
359 /***********************************************************************
360 * PROFILE_DeleteKey
362 * Delete a key from a profile tree.
364 static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
365 LPCSTR section_name, LPCSTR key_name )
367 while (*section)
369 if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
371 PROFILEKEY **key = &(*section)->key;
372 while (*key)
374 if (!strcasecmp( (*key)->name, key_name ))
376 PROFILEKEY *to_del = *key;
377 *key = to_del->next;
378 if (to_del->name) HeapFree( GetProcessHeap(), 0, to_del->name );
379 if (to_del->value) HeapFree( GetProcessHeap(), 0, to_del->value);
380 HeapFree( GetProcessHeap(), 0, to_del );
381 return TRUE;
383 key = &(*key)->next;
386 section = &(*section)->next;
388 return FALSE;
392 /***********************************************************************
393 * PROFILE_Find
395 * Find a key in a profile tree, optionally creating it.
397 static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
398 const char *section_name,
399 const char *key_name, int create )
401 const char *p;
402 int seclen, keylen;
404 while (PROFILE_isspace(*section_name)) section_name++;
405 p = section_name + strlen(section_name) - 1;
406 while ((p > section_name) && PROFILE_isspace(*p)) p--;
407 seclen = p - section_name + 1;
409 while (PROFILE_isspace(*key_name)) key_name++;
410 p = key_name + strlen(key_name) - 1;
411 while ((p > key_name) && PROFILE_isspace(*p)) p--;
412 keylen = p - key_name + 1;
414 while (*section)
416 if ( ((*section)->name)
417 && (!(strncasecmp( (*section)->name, section_name, seclen )))
418 && (((*section)->name)[seclen] == '\0') )
420 PROFILEKEY **key = &(*section)->key;
421 while (*key)
423 if ( (!(strncasecmp( (*key)->name, key_name, keylen )))
424 && (((*key)->name)[keylen] == '\0') )
425 return *key;
426 key = &(*key)->next;
428 if (!create) return NULL;
429 *key = HEAP_xalloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
430 (*key)->name = HEAP_strdupA( GetProcessHeap(), 0, key_name );
431 (*key)->value = NULL;
432 (*key)->next = NULL;
433 return *key;
435 section = &(*section)->next;
437 if (!create) return NULL;
438 *section = HEAP_xalloc( GetProcessHeap(), 0, sizeof(PROFILESECTION) );
439 (*section)->name = HEAP_strdupA( GetProcessHeap(), 0, section_name );
440 (*section)->next = NULL;
441 (*section)->key = HEAP_xalloc( GetProcessHeap(), 0, sizeof(PROFILEKEY) );
442 (*section)->key->name = HEAP_strdupA( GetProcessHeap(), 0, key_name );
443 (*section)->key->value = NULL;
444 (*section)->key->next = NULL;
445 return (*section)->key;
449 /***********************************************************************
450 * PROFILE_FlushFile
452 * Flush the current profile to disk if changed.
454 static BOOL PROFILE_FlushFile(void)
456 char *p, buffer[MAX_PATHNAME_LEN];
457 const char *unix_name;
458 FILE *file = NULL;
459 struct stat buf;
461 if(!CurProfile)
463 WARN("No current profile!\n");
464 return FALSE;
467 if (!CurProfile->changed || !CurProfile->dos_name) return TRUE;
468 if (!(unix_name = CurProfile->unix_name) || !(file = fopen(unix_name, "w")))
470 /* Try to create it in $HOME/.wine */
471 /* FIXME: this will need a more general solution */
472 strcpy( buffer, get_config_dir() );
473 p = buffer + strlen(buffer);
474 *p++ = '/';
475 strcpy( p, strrchr( CurProfile->dos_name, '\\' ) + 1 );
476 CharLowerA( p );
477 file = fopen( buffer, "w" );
478 unix_name = buffer;
481 if (!file)
483 WARN("could not save profile file %s\n", CurProfile->dos_name);
484 return FALSE;
487 TRACE("Saving '%s' into '%s'\n", CurProfile->dos_name, unix_name );
488 PROFILE_Save( file, CurProfile->section );
489 fclose( file );
490 CurProfile->changed = FALSE;
491 if(!stat(unix_name,&buf))
492 CurProfile->mtime=buf.st_mtime;
493 return TRUE;
497 /***********************************************************************
498 * PROFILE_ReleaseFile
500 * Flush the current profile to disk and remove it from the cache.
502 static void PROFILE_ReleaseFile(void)
504 PROFILE_FlushFile();
505 PROFILE_Free( CurProfile->section );
506 if (CurProfile->dos_name) HeapFree( GetProcessHeap(), 0, CurProfile->dos_name );
507 if (CurProfile->unix_name) HeapFree( GetProcessHeap(), 0, CurProfile->unix_name );
508 if (CurProfile->filename) HeapFree( GetProcessHeap(), 0, CurProfile->filename );
509 CurProfile->changed = FALSE;
510 CurProfile->section = NULL;
511 CurProfile->dos_name = NULL;
512 CurProfile->unix_name = NULL;
513 CurProfile->filename = NULL;
514 CurProfile->mtime = 0;
518 /***********************************************************************
519 * PROFILE_Open
521 * Open a profile file, checking the cached file first.
523 static BOOL PROFILE_Open( LPCSTR filename )
525 DOS_FULL_NAME full_name;
526 char buffer[MAX_PATHNAME_LEN];
527 char *newdos_name, *p;
528 FILE *file = NULL;
529 int i,j;
530 struct stat buf;
531 PROFILE *tempProfile;
533 /* First time around */
535 if(!CurProfile)
536 for(i=0;i<N_CACHED_PROFILES;i++)
538 MRUProfile[i]=HEAP_xalloc( GetProcessHeap(), 0, sizeof(PROFILE) );
539 MRUProfile[i]->changed=FALSE;
540 MRUProfile[i]->section=NULL;
541 MRUProfile[i]->dos_name=NULL;
542 MRUProfile[i]->unix_name=NULL;
543 MRUProfile[i]->filename=NULL;
544 MRUProfile[i]->mtime=0;
547 /* Check for a match */
549 if (strchr( filename, '/' ) || strchr( filename, '\\' ) ||
550 strchr( filename, ':' ))
552 if (!DOSFS_GetFullName( filename, FALSE, &full_name )) return FALSE;
554 else
556 GetWindowsDirectoryA( buffer, sizeof(buffer) );
557 strcat( buffer, "\\" );
558 strcat( buffer, filename );
559 if (!DOSFS_GetFullName( buffer, FALSE, &full_name )) return FALSE;
562 for(i=0;i<N_CACHED_PROFILES;i++)
564 if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )) ||
565 (MRUProfile[i]->dos_name && !strcmp( full_name.short_name, MRUProfile[i]->dos_name )))
567 if(i)
569 PROFILE_FlushFile();
570 tempProfile=MRUProfile[i];
571 for(j=i;j>0;j--)
572 MRUProfile[j]=MRUProfile[j-1];
573 CurProfile=tempProfile;
575 if(!stat(CurProfile->unix_name,&buf) && CurProfile->mtime==buf.st_mtime)
576 TRACE("(%s): already opened (mru=%d)\n",
577 filename, i );
578 else
579 TRACE("(%s): already opened, needs refreshing (mru=%d)\n",
580 filename, i );
581 return TRUE;
585 /* Flush the old current profile */
586 PROFILE_FlushFile();
588 /* Make the oldest profile the current one only in order to get rid of it */
589 if(i==N_CACHED_PROFILES)
591 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
592 for(i=N_CACHED_PROFILES-1;i>0;i--)
593 MRUProfile[i]=MRUProfile[i-1];
594 CurProfile=tempProfile;
596 if(CurProfile->filename) PROFILE_ReleaseFile();
598 /* OK, now that CurProfile is definitely free we assign it our new file */
599 newdos_name = HEAP_strdupA( GetProcessHeap(), 0, full_name.short_name );
600 CurProfile->dos_name = newdos_name;
601 CurProfile->filename = HEAP_strdupA( GetProcessHeap(), 0, filename );
603 /* Try to open the profile file, first in $HOME/.wine */
605 /* FIXME: this will need a more general solution */
606 strcpy( buffer, get_config_dir() );
607 p = buffer + strlen(buffer);
608 *p++ = '/';
609 strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
610 CharLowerA( p );
611 if ((file = fopen( buffer, "r" )))
613 TRACE("(%s): found it in %s\n",
614 filename, buffer );
615 CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0, buffer );
618 if (!file)
620 CurProfile->unix_name = HEAP_strdupA( GetProcessHeap(), 0,
621 full_name.long_name );
622 if ((file = fopen( full_name.long_name, "r" )))
623 TRACE("(%s): found it in %s\n",
624 filename, full_name.long_name );
627 if (file)
629 CurProfile->section = PROFILE_Load( file );
630 fclose( file );
631 if(!stat(CurProfile->unix_name,&buf))
632 CurProfile->mtime=buf.st_mtime;
634 else
636 /* Does not exist yet, we will create it in PROFILE_FlushFile */
637 WARN("profile file %s not found\n", newdos_name );
639 return TRUE;
643 /***********************************************************************
644 * PROFILE_GetSection
646 * Returns all keys of a section.
647 * If return_values is TRUE, also include the corresponding values.
649 static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
650 LPSTR buffer, UINT len, BOOL handle_env,
651 BOOL return_values )
653 PROFILEKEY *key;
654 while (section)
656 if (section->name && !strcasecmp( section->name, section_name ))
658 UINT oldlen = len;
659 for (key = section->key; key; key = key->next)
661 if (len <= 2) break;
662 if (!*key->name) continue; /* Skip empty lines */
663 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
664 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
665 len -= strlen(buffer) + 1;
666 buffer += strlen(buffer) + 1;
667 if (return_values && key->value) {
668 buffer[-1] = '=';
669 PROFILE_CopyEntry ( buffer,
670 key->value, len - 1, handle_env );
671 len -= strlen(buffer) + 1;
672 buffer += strlen(buffer) + 1;
675 *buffer = '\0';
676 if (len <= 1)
677 /*If either lpszSection or lpszKey is NULL and the supplied
678 destination buffer is too small to hold all the strings,
679 the last string is truncated and followed by two null characters.
680 In this case, the return value is equal to cchReturnBuffer
681 minus two. */
683 buffer[-1] = '\0';
684 return oldlen - 2;
686 return oldlen - len;
688 section = section->next;
690 buffer[0] = buffer[1] = '\0';
691 return 0;
695 static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
697 LPSTR buf = buffer;
698 WORD l, cursize = 0;
699 PROFILESECTION *section;
701 for (section = CurProfile->section; section; section = section->next)
702 if (section->name) {
703 l = strlen(section->name);
704 cursize += l+1;
705 if (cursize > len+1)
706 return len-2;
708 strcpy(buf, section->name);
709 buf += l+1;
712 *buf=0;
713 buf++;
714 return buf-buffer;
718 /***********************************************************************
719 * PROFILE_GetString
721 * Get a profile string.
723 static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
724 LPCSTR def_val, LPSTR buffer, UINT len )
726 PROFILEKEY *key = NULL;
728 if (!def_val) def_val = "";
729 if (key_name && key_name[0])
731 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
732 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
733 len, FALSE );
734 TRACE("('%s','%s','%s'): returning '%s'\n",
735 section, key_name, def_val, buffer );
736 return strlen( buffer );
738 if (key_name && !(key_name[0]))
739 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227*/
740 return 0;
741 if (section && section[0])
742 return PROFILE_GetSection(CurProfile->section, section, buffer, len,
743 FALSE, FALSE);
744 /* undocumented; both section and key_name are NULL */
745 return PROFILE_GetSectionNames(buffer, len);
749 /***********************************************************************
750 * PROFILE_SetString
752 * Set a profile string.
754 static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
755 LPCSTR value )
757 if (!key_name) /* Delete a whole section */
759 TRACE("('%s')\n", section_name);
760 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
761 section_name );
762 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
763 this is not an error on application's level.*/
765 else if (!value) /* Delete a key */
767 TRACE("('%s','%s')\n",
768 section_name, key_name );
769 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
770 section_name, key_name );
771 return TRUE; /* same error handling as above */
773 else /* Set the key value */
775 PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
776 key_name, TRUE );
777 TRACE("('%s','%s','%s'): \n",
778 section_name, key_name, value );
779 if (!key) return FALSE;
780 if (key->value)
782 if (!strcmp( key->value, value ))
784 TRACE(" no change needed\n" );
785 return TRUE; /* No change needed */
787 TRACE(" replacing '%s'\n", key->value );
788 HeapFree( GetProcessHeap(), 0, key->value );
790 else TRACE(" creating key\n" );
791 key->value = HEAP_strdupA( GetProcessHeap(), 0, value );
792 CurProfile->changed = TRUE;
794 return TRUE;
798 /***********************************************************************
799 * PROFILE_GetWineIniString
801 * Get a config string from the wine.ini file.
803 int PROFILE_GetWineIniString( const char *section, const char *key_name,
804 const char *def, char *buffer, int len )
806 char tmp[PROFILE_MAX_LINE_LEN];
807 HKEY hkey;
808 DWORD err;
810 if (!(err = RegOpenKeyA( wine_profile_key, section, &hkey )))
812 DWORD type;
813 DWORD count = sizeof(tmp);
814 err = RegQueryValueExA( hkey, key_name, 0, &type, tmp, &count );
815 RegCloseKey( hkey );
817 PROFILE_CopyEntry( buffer, err ? def : tmp, len, TRUE );
818 TRACE( "('%s','%s','%s'): returning '%s'\n", section, key_name, def, buffer );
819 return strlen(buffer);
823 /***********************************************************************
824 * PROFILE_EnumWineIniString
826 * Get a config string from the wine.ini file.
828 BOOL PROFILE_EnumWineIniString( const char *section, int index,
829 char *name, int name_len, char *buffer, int len )
831 char tmp[PROFILE_MAX_LINE_LEN];
832 HKEY hkey;
833 DWORD err, type;
834 DWORD count = sizeof(tmp);
836 if (RegOpenKeyA( wine_profile_key, section, &hkey )) return FALSE;
837 err = RegEnumValueA( hkey, index, name, (DWORD*)&name_len, NULL, &type, tmp, &count );
838 RegCloseKey( hkey );
839 if (!err)
841 PROFILE_CopyEntry( buffer, tmp, len, TRUE );
842 TRACE( "('%s',%d): returning '%s'='%s'\n", section, index, name, buffer );
844 return !err;
848 /***********************************************************************
849 * PROFILE_GetWineIniInt
851 * Get a config integer from the wine.ini file.
853 int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
855 char buffer[20];
856 char *p;
857 long result;
859 PROFILE_GetWineIniString( section, key_name, "", buffer, sizeof(buffer) );
860 if (!buffer[0]) return def;
861 result = strtol( buffer, &p, 0 );
862 return (p == buffer) ? 0 /* No digits at all */ : (int)result;
866 /******************************************************************************
868 * int PROFILE_GetWineIniBool(
869 * char const *section,
870 * char const *key_name,
871 * int def )
873 * Reads a boolean value from the wine.ini file. This function attempts to
874 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
875 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
876 * true. Anything else results in the return of the default value.
878 * This function uses 1 to indicate true, and 0 for false. You can check
879 * for existence by setting def to something other than 0 or 1 and
880 * examining the return value.
882 int PROFILE_GetWineIniBool(
883 char const *section,
884 char const *key_name,
885 int def )
887 char key_value[2];
888 int retval;
890 PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
892 switch(key_value[0]) {
893 case 'n':
894 case 'N':
895 case 'f':
896 case 'F':
897 case '0':
898 retval = 0;
899 break;
901 case 'y':
902 case 'Y':
903 case 't':
904 case 'T':
905 case '1':
906 retval = 1;
907 break;
909 default:
910 retval = def;
913 TRACE("(\"%s\", \"%s\", %s), "
914 "[%c], ret %s.\n", section, key_name,
915 def ? "TRUE" : "FALSE", key_value[0],
916 retval ? "TRUE" : "FALSE");
918 return retval;
922 /***********************************************************************
923 * PROFILE_LoadWineIni
925 * Load the wine.ini file.
927 int PROFILE_LoadWineIni(void)
929 char buffer[MAX_PATHNAME_LEN];
930 const char *p;
931 FILE *f;
932 HKEY hKeySW;
934 /* make sure HKLM\\Software exists as non-volatile key */
935 if (RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software", &hKeySW ))
937 ERR("Cannot create config registry key\n" );
938 return 0;
940 RegCloseKey( hKeySW );
941 if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config", 0, NULL,
942 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &wine_profile_key, NULL ))
944 ERR("Cannot create config registry key\n" );
945 return 0;
947 wine_profile_key = ConvertToGlobalHandle( wine_profile_key );
949 InitializeCriticalSection( &PROFILE_CritSect );
950 MakeCriticalSectionGlobal( &PROFILE_CritSect );
952 if (!CLIENT_IsBootThread()) return 1; /* already loaded */
954 if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) )
956 /* Open -config specified file */
957 lstrcpynA(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN);
958 goto found;
961 if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) )
963 lstrcpynA(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN);
964 goto found;
966 if ((p = getenv( "HOME" )) != NULL)
968 lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
969 strcat( buffer, PROFILE_WineIniName );
970 if ((f = fopen( buffer, "r" )) != NULL)
972 lstrcpynA(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN);
973 goto found;
976 else WARN("could not get $HOME value for config file.\n" );
978 /* Try global file */
980 if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
982 lstrcpynA(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN);
983 goto found;
985 MESSAGE( "Can't open configuration file %s or $HOME%s\n",
986 WINE_INI_GLOBAL, PROFILE_WineIniName );
987 return 0;
989 found:
990 PROFILE_RegistryLoad( wine_profile_key, f );
991 fclose( f );
992 return 1;
996 /***********************************************************************
997 * PROFILE_UsageWineIni
999 * Explain the wine.ini file to those who don't read documentation.
1000 * Keep below one screenful in length so that error messages above are
1001 * noticed.
1003 void PROFILE_UsageWineIni(void)
1005 MESSAGE("Perhaps you have not properly edited or created "
1006 "your Wine configuration file.\n");
1007 MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName);
1008 MESSAGE(" or it is determined by the -config option or from\n"
1009 " the WINE_INI environment variable.\n");
1010 if (*PROFILE_WineIniUsed)
1011 MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed);
1012 /* RTFM, so to say */
1015 /***********************************************************************
1016 * PROFILE_GetStringItem
1018 * Convenience function that turns a string 'xxx, yyy, zzz' into
1019 * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1021 char* PROFILE_GetStringItem( char* start )
1023 char* lpchX, *lpch;
1025 for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
1027 if( *lpchX == ',' )
1029 if( lpch ) *lpch = '\0'; else *lpchX = '\0';
1030 while( *(++lpchX) )
1031 if( !PROFILE_isspace(*lpchX) ) return lpchX;
1033 else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
1034 else lpch = NULL;
1036 if( lpch ) *lpch = '\0';
1037 return NULL;
1040 /********************* API functions **********************************/
1042 /***********************************************************************
1043 * GetProfileInt16 (KERNEL.57)
1045 UINT16 WINAPI GetProfileInt16( LPCSTR section, LPCSTR entry, INT16 def_val )
1047 return GetPrivateProfileInt16( section, entry, def_val, "win.ini" );
1051 /***********************************************************************
1052 * GetProfileIntA (KERNEL32.264)
1054 UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
1056 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
1059 /***********************************************************************
1060 * GetProfileIntW (KERNEL32.264)
1062 UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
1064 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
1067 /***********************************************************************
1068 * GetProfileString16 (KERNEL.58)
1070 INT16 WINAPI GetProfileString16( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1071 LPSTR buffer, UINT16 len )
1073 return GetPrivateProfileString16( section, entry, def_val,
1074 buffer, len, "win.ini" );
1077 /***********************************************************************
1078 * GetProfileStringA (KERNEL32.268)
1080 INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
1081 LPSTR buffer, UINT len )
1083 return GetPrivateProfileStringA( section, entry, def_val,
1084 buffer, len, "win.ini" );
1087 /***********************************************************************
1088 * GetProfileStringW (KERNEL32.269)
1090 INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1091 LPCWSTR def_val, LPWSTR buffer, UINT len )
1093 return GetPrivateProfileStringW( section, entry, def_val,
1094 buffer, len, wininiW );
1097 /***********************************************************************
1098 * WriteProfileString16 (KERNEL.59)
1100 BOOL16 WINAPI WriteProfileString16( LPCSTR section, LPCSTR entry,
1101 LPCSTR string )
1103 return WritePrivateProfileString16( section, entry, string, "win.ini" );
1106 /***********************************************************************
1107 * WriteProfileStringA (KERNEL32.587)
1109 BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1110 LPCSTR string )
1112 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1115 /***********************************************************************
1116 * WriteProfileStringW (KERNEL32.588)
1118 BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1119 LPCWSTR string )
1121 return WritePrivateProfileStringW( section, entry, string, wininiW );
1125 /***********************************************************************
1126 * GetPrivateProfileInt16 (KERNEL.127)
1128 UINT16 WINAPI GetPrivateProfileInt16( LPCSTR section, LPCSTR entry,
1129 INT16 def_val, LPCSTR filename )
1131 long result=(long)GetPrivateProfileIntA(section,entry,def_val,filename);
1133 if (result > 65535) return 65535;
1134 if (result >= 0) return (UINT16)result;
1135 if (result < -32768) return -32768;
1136 return (UINT16)(INT16)result;
1139 /***********************************************************************
1140 * GetPrivateProfileIntA (KERNEL32.251)
1142 UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1143 INT def_val, LPCSTR filename )
1145 char buffer[20];
1146 char *p;
1147 long result;
1149 GetPrivateProfileStringA( section, entry, "",
1150 buffer, sizeof(buffer), filename );
1151 if (!buffer[0]) return (UINT)def_val;
1152 result = strtol( buffer, &p, 0 );
1153 if (p == buffer) return 0; /* No digits at all */
1154 return (UINT)result;
1157 /***********************************************************************
1158 * GetPrivateProfileIntW (KERNEL32.252)
1160 UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1161 INT def_val, LPCWSTR filename )
1163 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1164 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1165 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1166 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1167 HeapFree( GetProcessHeap(), 0, sectionA );
1168 HeapFree( GetProcessHeap(), 0, filenameA );
1169 HeapFree( GetProcessHeap(), 0, entryA );
1170 return res;
1173 /***********************************************************************
1174 * GetPrivateProfileString16 (KERNEL.128)
1176 INT16 WINAPI GetPrivateProfileString16( LPCSTR section, LPCSTR entry,
1177 LPCSTR def_val, LPSTR buffer,
1178 UINT16 len, LPCSTR filename )
1180 return GetPrivateProfileStringA(section,entry,def_val,buffer,len,filename);
1183 /***********************************************************************
1184 * GetPrivateProfileStringA (KERNEL32.255)
1186 INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1187 LPCSTR def_val, LPSTR buffer,
1188 UINT len, LPCSTR filename )
1190 int ret;
1192 if (!filename)
1193 filename = "win.ini";
1195 EnterCriticalSection( &PROFILE_CritSect );
1197 if (PROFILE_Open( filename )) {
1198 ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1199 } else {
1200 lstrcpynA( buffer, def_val, len );
1201 ret = strlen( buffer );
1204 LeaveCriticalSection( &PROFILE_CritSect );
1206 return ret;
1209 /***********************************************************************
1210 * GetPrivateProfileStringW (KERNEL32.256)
1212 INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1213 LPCWSTR def_val, LPWSTR buffer,
1214 UINT len, LPCWSTR filename )
1216 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1217 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1218 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1219 LPSTR def_valA = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1220 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1221 INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1222 bufferA, len, filenameA );
1223 lstrcpynAtoW( buffer, bufferA, len );
1224 HeapFree( GetProcessHeap(), 0, sectionA );
1225 HeapFree( GetProcessHeap(), 0, entryA );
1226 HeapFree( GetProcessHeap(), 0, filenameA );
1227 HeapFree( GetProcessHeap(), 0, def_valA );
1228 HeapFree( GetProcessHeap(), 0, bufferA);
1229 return ret;
1232 /***********************************************************************
1233 * GetPrivateProfileSection16 (KERNEL.418)
1235 INT16 WINAPI GetPrivateProfileSection16( LPCSTR section, LPSTR buffer,
1236 UINT16 len, LPCSTR filename )
1238 return GetPrivateProfileSectionA( section, buffer, len, filename );
1241 /***********************************************************************
1242 * GetPrivateProfileSectionA (KERNEL32.255)
1244 INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1245 DWORD len, LPCSTR filename )
1247 int ret = 0;
1249 EnterCriticalSection( &PROFILE_CritSect );
1251 if (PROFILE_Open( filename ))
1252 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1253 FALSE, TRUE);
1255 LeaveCriticalSection( &PROFILE_CritSect );
1257 return ret;
1260 /***********************************************************************
1261 * GetPrivateProfileSectionW (KERNEL32.256)
1264 INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1265 DWORD len, LPCWSTR filename )
1268 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1269 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1270 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1271 INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len,
1272 filenameA );
1273 MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1274 HeapFree( GetProcessHeap(), 0, sectionA );
1275 HeapFree( GetProcessHeap(), 0, filenameA );
1276 HeapFree( GetProcessHeap(), 0, bufferA);
1277 return ret;
1280 /***********************************************************************
1281 * GetProfileSection16 (KERNEL.419)
1283 INT16 WINAPI GetProfileSection16( LPCSTR section, LPSTR buffer, UINT16 len )
1285 return GetPrivateProfileSection16( section, buffer, len, "win.ini" );
1288 /***********************************************************************
1289 * GetProfileSectionA (KERNEL32.268)
1291 INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1293 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1296 /***********************************************************************
1297 * GetProfileSectionW (KERNEL32)
1299 INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1301 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1305 /***********************************************************************
1306 * WritePrivateProfileString16 (KERNEL.129)
1308 BOOL16 WINAPI WritePrivateProfileString16( LPCSTR section, LPCSTR entry,
1309 LPCSTR string, LPCSTR filename )
1311 return WritePrivateProfileStringA(section,entry,string,filename);
1314 /***********************************************************************
1315 * WritePrivateProfileStringA (KERNEL32.582)
1317 BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1318 LPCSTR string, LPCSTR filename )
1320 BOOL ret = FALSE;
1322 EnterCriticalSection( &PROFILE_CritSect );
1324 if (PROFILE_Open( filename ))
1326 if (!section && !entry && !string)
1327 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1328 else
1329 ret = PROFILE_SetString( section, entry, string );
1332 LeaveCriticalSection( &PROFILE_CritSect );
1333 return ret;
1336 /***********************************************************************
1337 * WritePrivateProfileStringW (KERNEL32.583)
1339 BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1340 LPCWSTR string, LPCWSTR filename )
1342 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1343 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1344 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1345 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1346 BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1347 stringA, filenameA );
1348 HeapFree( GetProcessHeap(), 0, sectionA );
1349 HeapFree( GetProcessHeap(), 0, entryA );
1350 HeapFree( GetProcessHeap(), 0, stringA );
1351 HeapFree( GetProcessHeap(), 0, filenameA );
1352 return res;
1355 /***********************************************************************
1356 * WritePrivateProfileSection16 (KERNEL.416)
1358 BOOL16 WINAPI WritePrivateProfileSection16( LPCSTR section,
1359 LPCSTR string, LPCSTR filename )
1361 return WritePrivateProfileSectionA( section, string, filename );
1364 /***********************************************************************
1365 * WritePrivateProfileSectionA (KERNEL32)
1367 BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1368 LPCSTR string, LPCSTR filename )
1370 BOOL ret = FALSE;
1371 LPSTR p ;
1373 EnterCriticalSection( &PROFILE_CritSect );
1375 if (PROFILE_Open( filename )) {
1376 if (!section && !string && !filename)
1377 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1378 else {
1379 while(*string){
1380 LPSTR buf=HEAP_strdupA( GetProcessHeap(), 0, string );
1381 if((p=strchr( buf, '='))){
1382 *p='\0';
1383 ret = PROFILE_SetString( section, buf, p+1 );
1386 HeapFree( GetProcessHeap(), 0, buf );
1387 string += strlen(string)+1;
1393 LeaveCriticalSection( &PROFILE_CritSect );
1394 return ret;
1397 /***********************************************************************
1398 * WritePrivateProfileSectionW (KERNEL32)
1400 BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1401 LPCWSTR string, LPCWSTR filename)
1404 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1405 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1406 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1407 BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1408 HeapFree( GetProcessHeap(), 0, sectionA );
1409 HeapFree( GetProcessHeap(), 0, stringA );
1410 HeapFree( GetProcessHeap(), 0, filenameA );
1411 return res;
1414 /***********************************************************************
1415 * WriteProfileSection16 (KERNEL.417)
1417 BOOL16 WINAPI WriteProfileSection16( LPCSTR section, LPCSTR keys_n_values)
1419 return WritePrivateProfileSection16( section, keys_n_values, "win.ini");
1422 /***********************************************************************
1423 * WriteProfileSectionA (KERNEL32.747)
1425 BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1428 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1431 /***********************************************************************
1432 * WriteProfileSectionW (KERNEL32.748)
1434 BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1436 return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1439 /***********************************************************************
1440 * GetPrivateProfileSectionNames16 (KERNEL.143)
1442 WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1443 LPCSTR filename )
1445 WORD ret = 0;
1447 EnterCriticalSection( &PROFILE_CritSect );
1449 if (PROFILE_Open( filename ))
1450 ret = PROFILE_GetSectionNames(buffer, size);
1452 LeaveCriticalSection( &PROFILE_CritSect );
1454 return ret;
1458 /***********************************************************************
1459 * GetProfileSectionNames16 (KERNEL.142)
1461 WORD WINAPI GetProfileSectionNames16( LPSTR buffer, WORD size)
1464 return (GetPrivateProfileSectionNames16 (buffer,size,"win.ini"));
1468 /***********************************************************************
1469 * GetPrivateProfileSectionNamesA (KERNEL32.365)
1471 DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1472 LPCSTR filename)
1475 return (GetPrivateProfileSectionNames16 (buffer,size,filename));
1479 /***********************************************************************
1480 * GetPrivateProfileSectionNamesW (KERNEL32.366)
1482 DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1483 LPCWSTR filename)
1486 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1487 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, size);
1489 INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
1490 lstrcpynAtoW( buffer, bufferA, size);
1491 HeapFree( GetProcessHeap(), 0, bufferA);
1492 HeapFree( GetProcessHeap(), 0, filenameA );
1494 return ret;
1497 /***********************************************************************
1498 * GetPrivateProfileStruct16 (KERNEL.407)
1500 BOOL16 WINAPI GetPrivateProfileStruct16(LPCSTR section, LPCSTR key,
1501 LPVOID buf, UINT16 len, LPCSTR filename)
1503 return GetPrivateProfileStructA( section, key, buf, len, filename );
1506 /***********************************************************************
1507 * GetPrivateProfileStructA (KERNEL32.370)
1509 * Should match Win95's behaviour pretty much
1511 BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1512 LPVOID buf, UINT len, LPCSTR filename)
1514 BOOL ret = FALSE;
1516 EnterCriticalSection( &PROFILE_CritSect );
1518 if (PROFILE_Open( filename )) {
1519 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1520 if (k) {
1521 TRACE("value (at %p): '%s'\n", k->value, k->value);
1522 if (((strlen(k->value) - 2) / 2) == len)
1524 LPSTR end, p;
1525 BOOL valid = TRUE;
1526 CHAR c;
1527 DWORD chksum = 0;
1529 end = k->value + strlen(k->value); /* -> '\0' */
1530 /* check for invalid chars in ASCII coded hex string */
1531 for (p=k->value; p < end; p++)
1533 if (!isxdigit(*p))
1535 WARN("invalid char '%c' in file '%s'->'[%s]'->'%s' !\n",
1536 *p, filename, section, key);
1537 valid = FALSE;
1538 break;
1541 if (valid)
1543 BOOL highnibble = TRUE;
1544 BYTE b = 0, val;
1545 LPBYTE binbuf = (LPBYTE)buf;
1547 end -= 2; /* don't include checksum in output data */
1548 /* translate ASCII hex format into binary data */
1549 for (p=k->value; p < end; p++)
1551 c = toupper(*p);
1552 val = (c > '9') ?
1553 (c - 'A' + 10) : (c - '0');
1555 if (highnibble)
1556 b = val << 4;
1557 else
1559 b += val;
1560 *binbuf++ = b; /* feed binary data into output */
1561 chksum += b; /* calculate checksum */
1563 highnibble ^= 1; /* toggle */
1565 /* retrieve stored checksum value */
1566 c = toupper(*p++);
1567 b = ( (c > '9') ? (c - 'A' + 10) : (c - '0') ) << 4;
1568 c = toupper(*p);
1569 b += (c > '9') ? (c - 'A' + 10) : (c - '0');
1570 if (b == (chksum & 0xff)) /* checksums match ? */
1571 ret = TRUE;
1576 LeaveCriticalSection( &PROFILE_CritSect );
1578 return ret;
1581 /***********************************************************************
1582 * GetPrivateProfileStructW (KERNEL32.543)
1584 BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1585 LPVOID buffer, UINT len, LPCWSTR filename)
1587 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1588 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1589 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1590 LPSTR bufferA = HeapAlloc( GetProcessHeap(), 0, len );
1592 INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1593 len, filenameA );
1594 lstrcpynAtoW( buffer, bufferA, len );
1595 HeapFree( GetProcessHeap(), 0, bufferA);
1596 HeapFree( GetProcessHeap(), 0, sectionA );
1597 HeapFree( GetProcessHeap(), 0, keyA );
1598 HeapFree( GetProcessHeap(), 0, filenameA );
1600 return ret;
1605 /***********************************************************************
1606 * WritePrivateProfileStruct16 (KERNEL.406)
1608 BOOL16 WINAPI WritePrivateProfileStruct16 (LPCSTR section, LPCSTR key,
1609 LPVOID buf, UINT16 bufsize, LPCSTR filename)
1611 return WritePrivateProfileStructA( section, key, buf, bufsize, filename );
1614 /***********************************************************************
1615 * WritePrivateProfileStructA (KERNEL32.744)
1617 BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1618 LPVOID buf, UINT bufsize, LPCSTR filename)
1620 BOOL ret = FALSE;
1621 LPBYTE binbuf;
1622 LPSTR outstring, p;
1623 DWORD sum = 0;
1625 if (!section && !key && !buf) /* flush the cache */
1626 return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1628 /* allocate string buffer for hex chars + checksum hex char + '\0' */
1629 outstring = HeapAlloc( GetProcessHeap(), 0, bufsize*2 + 2 + 1);
1630 p = outstring;
1631 for (binbuf = (LPBYTE)buf; binbuf < (LPBYTE)buf+bufsize; binbuf++) {
1632 *p++ = hex[*binbuf >> 4];
1633 *p++ = hex[*binbuf & 0xf];
1634 sum += *binbuf;
1636 /* checksum is sum & 0xff */
1637 *p++ = hex[(sum & 0xf0) >> 4];
1638 *p++ = hex[sum & 0xf];
1639 *p++ = '\0';
1641 EnterCriticalSection( &PROFILE_CritSect );
1643 if (PROFILE_Open( filename ))
1644 ret = PROFILE_SetString( section, key, outstring );
1646 LeaveCriticalSection( &PROFILE_CritSect );
1648 HeapFree( GetProcessHeap(), 0, outstring );
1650 return ret;
1653 /***********************************************************************
1654 * WritePrivateProfileStructW (KERNEL32.544)
1656 BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1657 LPVOID buf, UINT bufsize, LPCWSTR filename)
1659 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1660 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1661 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1662 INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1663 filenameA );
1664 HeapFree( GetProcessHeap(), 0, sectionA );
1665 HeapFree( GetProcessHeap(), 0, keyA );
1666 HeapFree( GetProcessHeap(), 0, filenameA );
1668 return ret;
1672 /***********************************************************************
1673 * WriteOutProfiles (KERNEL.315)
1675 void WINAPI WriteOutProfiles16(void)
1677 EnterCriticalSection( &PROFILE_CritSect );
1678 PROFILE_FlushFile();
1679 LeaveCriticalSection( &PROFILE_CritSect );
1682 /***********************************************************************
1683 * CloseProfileUserMapping (KERNEL.138)
1685 BOOL WINAPI CloseProfileUserMapping(void) {
1686 FIXME("(), stub!\n");
1687 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1688 return FALSE;