source: trunk/src/kernel32/profile.cpp@ 414

Last change on this file since 414 was 414, checked in by phaller, 26 years ago

Fix: small fix in Profile handling

File size: 42.3 KB
Line 
1/* $Id: profile.cpp,v 1.6 1999-08-04 14:37:52 phaller Exp $ */
2
3/*
4 * Project Odin Software License can be found in LICENSE.TXT
5 *
6 * Win32 profile API functions
7 *
8 * Copyright 1993 Miguel de Icaza
9 * Copyright 1996 Alexandre Julliard
10 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
11 * Copyright 1998 Patrick Haller
12 */
13
14#include <os2win.h>
15#include <ctype.h>
16#include <stdlib.h>
17#include <string.h>
18#include <stdio.h>
19#include <unicode.h>
20//#include "heap.h"
21
22#include <sys/stat.h>
23
24#include "winbase.h"
25//#include "wine/winbase16.h"
26#include "winuser.h"
27#include "winnls.h"
28//#include "heap.h"
29//#include "debugtools.h"
30//#include "options.h"
31
32
33// -------------------------
34// WINE compatibility macros
35// -------------------------
36
37#ifndef MAX_PATHNAME_LEN
38#define MAX_PATHNAME_LEN 260
39#endif
40
41#define HeapFree(a,b,c) free(c)
42#define HEAP_xalloc(a,b,c) malloc(c)
43#define HEAP_strdupA(a,b,c) strdup(c)
44#define strcasecmp strcmp
45#define DOSFS_GetFullName(a,b,c) strcpy(c,a)
46#define HEAP_strdupAtoW(a,b,c) AsciiToUnicodeString((char *)c)
47#define HEAP_strdupWtoA(a,b,c) UnicodeToAsciiString((LPWSTR)c)
48#define lstrcpynAtoW(a,b,c) AsciiToUnicodeN((char*)a,(LPWSTR)b,(int)c)
49#define lstrcpynWtoA(a,b,c) UnicodeToAsciiN((LPWSTR)a,(char*)b,(int)c)
50#define lstrcpynA(a,b,c) strncpy((char*)a,(char*)b,(int)c)
51#define CharLowerA(a) (a)
52
53
54typedef struct tagPROFILEKEY
55{
56 char *name;
57 char *value;
58 struct tagPROFILEKEY *next;
59} PROFILEKEY;
60
61typedef struct tagPROFILESECTION
62{
63 char *name;
64 struct tagPROFILEKEY *key;
65 struct tagPROFILESECTION *next;
66} PROFILESECTION;
67
68
69typedef struct
70{
71 BOOL changed;
72 PROFILESECTION *section;
73 char *filename;
74 time_t mtime;
75} PROFILE;
76
77
78#define N_CACHED_PROFILES 10
79
80/* Cached profile files */
81static PROFILE *MRUProfile[N_CACHED_PROFILES]={NULL};
82
83#define CurProfile (MRUProfile[0])
84
85/* wine.ini profile content */
86static PROFILESECTION *PROFILE_WineProfile;
87
88#define PROFILE_MAX_LINE_LEN 1024
89
90/* Wine profile name in $HOME directory; must begin with slash */
91static const char PROFILE_WineIniName[] = "/.winerc";
92
93/* Wine profile: the profile file being used */
94static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
95
96/* Check for comments in profile */
97#define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
98
99#define WINE_INI_GLOBAL ETCDIR "/wine.conf"
100
101static LPCWSTR wininiW = NULL;
102
103static CRITICAL_SECTION PROFILE_CritSect;
104
105
106/***********************************************************************
107 * PROFILE_CopyEntry
108 *
109 * Copy the content of an entry into a buffer, removing quotes, and possibly
110 * translating environment variables.
111 */
112static void PROFILE_CopyEntry( char *buffer,
113 const char *value,
114 int len,
115 int handle_env )
116{
117 char quote = '\0';
118 const char *p;
119
120 if ((*value == '\'') || (*value == '\"'))
121 {
122 if (value[1] && (value[strlen(value)-1] == *value)) quote = *value++;
123 }
124
125 if (!handle_env)
126 {
127 lstrcpynA( buffer, value, len );
128 if (quote && (len >= strlen(value))) buffer[strlen(buffer)-1] = '\0';
129 return;
130 }
131
132 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len-- )
133 {
134 if ((*p == '$') && (p[1] == '{'))
135 {
136 char env_val[1024];
137 const char *env_p;
138 const char *p2 = strchr( p, '}' );
139 if (!p2) continue; /* ignore it */
140 lstrcpynA(env_val, p + 2, MIN( sizeof(env_val), (int)(p2-p)-1 ));
141 if ((env_p = getenv( env_val )) != NULL)
142 {
143 lstrcpynA( buffer, env_p, len );
144 buffer += strlen( buffer );
145 len -= strlen( buffer );
146 }
147 p = p2 + 1;
148 }
149 }
150 *buffer = '\0';
151}
152
153
154/***********************************************************************
155 * PROFILE_Save
156 *
157 * Save a profile tree to a file.
158 */
159static void PROFILE_Save( FILE *file, PROFILESECTION *section )
160{
161 PROFILEKEY *key;
162
163 for ( ; section; section = section->next)
164 {
165 if (section->name) fprintf( file, "\r\n[%s]\r\n", section->name );
166 for (key = section->key; key; key = key->next)
167 {
168 fprintf( file, "%s", key->name );
169 if (key->value) fprintf( file, "=%s", key->value );
170 fprintf( file, "\r\n" );
171 }
172 }
173}
174
175
176/***********************************************************************
177 * PROFILE_Free
178 *
179 * Free a profile tree.
180 */
181static void PROFILE_Free( PROFILESECTION *section )
182{
183 PROFILESECTION *next_section;
184 PROFILEKEY *key, *next_key;
185
186 for ( ; section; section = next_section)
187 {
188 if (section->name) HeapFree( SystemHeap, 0, section->name );
189 for (key = section->key; key; key = next_key)
190 {
191 next_key = key->next;
192 if (key->name) HeapFree( SystemHeap, 0, key->name );
193 if (key->value) HeapFree( SystemHeap, 0, key->value );
194 HeapFree( SystemHeap, 0, key );
195 }
196 next_section = section->next;
197 HeapFree( SystemHeap, 0, section );
198 }
199}
200
201static int
202PROFILE_isspace(char c) {
203 if (isspace(c)) return 1;
204 if (c=='\r' || c==0x1a) return 1;
205 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
206 return 0;
207}
208
209
210/***********************************************************************
211 * PROFILE_Load
212 *
213 * Load a profile tree from a file.
214 */
215static PROFILESECTION *PROFILE_Load( FILE *file )
216{
217 char buffer[PROFILE_MAX_LINE_LEN];
218 char *p, *p2;
219 int line = 0;
220 PROFILESECTION *section, *first_section;
221 PROFILESECTION **next_section;
222 PROFILEKEY *key, *prev_key, **next_key;
223
224 first_section = (PROFILESECTION *)HEAP_xalloc( SystemHeap, 0, sizeof(*section) );
225 first_section->name = NULL;
226 first_section->key = NULL;
227 first_section->next = NULL;
228 next_section = &first_section->next;
229 next_key = &first_section->key;
230 prev_key = NULL;
231
232 while (fgets( buffer, PROFILE_MAX_LINE_LEN, file ))
233 {
234 line++;
235 p = buffer;
236 while (*p && PROFILE_isspace(*p)) p++;
237 if (*p == '[') /* section start */
238 {
239 if (!(p2 = strrchr( p, ']' )))
240 {
241 dprintf(("Kernel32:Profile:Invalid section header at line %d: '%s'\n",
242 line, p ));
243 }
244 else
245 {
246 *p2 = '\0';
247 p++;
248 section = (PROFILESECTION*)HEAP_xalloc( SystemHeap, 0, sizeof(*section) );
249 section->name = HEAP_strdupA( SystemHeap, 0, p );
250 section->key = NULL;
251 section->next = NULL;
252 *next_section = section;
253 next_section = &section->next;
254 next_key = &section->key;
255 prev_key = NULL;
256
257 dprintf(("Kernel32:Profile:New section: '%s'\n",section->name));
258
259 continue;
260 }
261 }
262
263 p2=p+strlen(p) - 1;
264 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2))) *p2--='\0';
265
266 if ((p2 = strchr( p, '=' )) != NULL)
267 {
268 char *p3 = p2 - 1;
269 while ((p3 > p) && PROFILE_isspace(*p3)) *p3-- = '\0';
270 *p2++ = '\0';
271 while (*p2 && PROFILE_isspace(*p2)) p2++;
272 }
273
274 if(*p || !prev_key || *prev_key->name)
275 {
276 key = (PROFILEKEY*)HEAP_xalloc( SystemHeap, 0, sizeof(*key) );
277 key->name = HEAP_strdupA( SystemHeap, 0, p );
278 key->value = p2 ? HEAP_strdupA( SystemHeap, 0, p2 ) : NULL;
279 key->next = NULL;
280 *next_key = key;
281 next_key = &key->next;
282 prev_key = key;
283
284 dprintf(("Kernel32:Profile:New key: name='%s', value='%s'\n",key->name,key->value?key->value:"(none)"));
285 }
286 }
287 return first_section;
288}
289
290
291/***********************************************************************
292 * PROFILE_DeleteSection
293 *
294 * Delete a section from a profile tree.
295 */
296static BOOL PROFILE_DeleteSection( PROFILESECTION **section, LPCSTR name )
297{
298 while (*section)
299 {
300 if ((*section)->name && !strcasecmp( (*section)->name, name ))
301 {
302 PROFILESECTION *to_del = *section;
303 *section = to_del->next;
304 to_del->next = NULL;
305 PROFILE_Free( to_del );
306 return TRUE;
307 }
308 section = &(*section)->next;
309 }
310 return FALSE;
311}
312
313
314/***********************************************************************
315 * PROFILE_DeleteKey
316 *
317 * Delete a key from a profile tree.
318 */
319static BOOL PROFILE_DeleteKey( PROFILESECTION **section,
320 LPCSTR section_name, LPCSTR key_name )
321{
322 while (*section)
323 {
324 if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
325 {
326 PROFILEKEY **key = &(*section)->key;
327 while (*key)
328 {
329 if (!strcasecmp( (*key)->name, key_name ))
330 {
331 PROFILEKEY *to_del = *key;
332 *key = to_del->next;
333 if (to_del->name) HeapFree( SystemHeap, 0, to_del->name );
334 if (to_del->value) HeapFree( SystemHeap, 0, to_del->value);
335 HeapFree( SystemHeap, 0, to_del );
336 return TRUE;
337 }
338 key = &(*key)->next;
339 }
340 }
341 section = &(*section)->next;
342 }
343 return FALSE;
344}
345
346
347/***********************************************************************
348 * PROFILE_Find
349 *
350 * Find a key in a profile tree, optionally creating it.
351 */
352static PROFILEKEY *PROFILE_Find( PROFILESECTION **section,
353 const char *section_name,
354 const char *key_name, int create )
355{
356 while (*section)
357 {
358 if ((*section)->name && !strcasecmp( (*section)->name, section_name ))
359 {
360 PROFILEKEY **key = &(*section)->key;
361 while (*key)
362 {
363 if (!strcasecmp( (*key)->name, key_name )) return *key;
364 key = &(*key)->next;
365 }
366 if (!create) return NULL;
367 *key = (PROFILEKEY*)HEAP_xalloc( SystemHeap, 0, sizeof(PROFILEKEY) );
368 (*key)->name = HEAP_strdupA( SystemHeap, 0, key_name );
369 (*key)->value = NULL;
370 (*key)->next = NULL;
371 return *key;
372 }
373 section = &(*section)->next;
374 }
375 if (!create) return NULL;
376 *section = (PROFILESECTION*)HEAP_xalloc( SystemHeap, 0, sizeof(PROFILESECTION) );
377 (*section)->name = HEAP_strdupA( SystemHeap, 0, section_name );
378 (*section)->next = NULL;
379 (*section)->key = (tagPROFILEKEY*)HEAP_xalloc( SystemHeap, 0, sizeof(PROFILEKEY) );
380 (*section)->key->name = HEAP_strdupA( SystemHeap, 0, key_name );
381 (*section)->key->value = NULL;
382 (*section)->key->next = NULL;
383 return (*section)->key;
384}
385
386
387/***********************************************************************
388 * PROFILE_FlushFile
389 *
390 * Flush the current profile to disk if changed.
391 */
392static BOOL PROFILE_FlushFile(void)
393{
394 FILE *file = NULL;
395 struct stat buf;
396
397 if(!CurProfile)
398 {
399 dprintf(("Kernel32:Profile:No current profile!\n"));
400 return FALSE;
401 }
402
403 // not changed, return immediately
404 if (!CurProfile->changed)
405 return TRUE;
406
407 // try to open file
408 file = fopen(CurProfile->filename, "w");
409 if (!file)
410 {
411 dprintf(("Kernel32:Profile:could not save profile file %s\n", CurProfile->filename));
412 return FALSE;
413 }
414
415 dprintf(("Kernel32:Profile:Saving %s\n", CurProfile->filename ));
416 PROFILE_Save( file, CurProfile->section );
417 fclose( file );
418 CurProfile->changed = FALSE;
419 if(!stat(CurProfile->filename,&buf))
420 CurProfile->mtime=buf.st_mtime;
421 return TRUE;
422}
423
424
425/***********************************************************************
426 * PROFILE_ReleaseFile
427 *
428 * Flush the current profile to disk and remove it from the cache.
429 */
430static void PROFILE_ReleaseFile(void)
431{
432 PROFILE_FlushFile();
433 PROFILE_Free( CurProfile->section );
434 if (CurProfile->filename) HeapFree( SystemHeap, 0, CurProfile->filename );
435 CurProfile->changed = FALSE;
436 CurProfile->section = NULL;
437 CurProfile->filename = NULL;
438 CurProfile->mtime = 0;
439}
440
441
442/***********************************************************************
443 * PROFILE_Open
444 *
445 * Open a profile file, checking the cached file first.
446 */
447static BOOL PROFILE_Open( LPCSTR filename )
448{
449 char buffer[MAX_PATHNAME_LEN];
450 FILE *file = NULL;
451 int i,j;
452 struct stat buf;
453 PROFILE *tempProfile;
454
455 /* First time around */
456
457 if(!CurProfile)
458 for(i=0;i<N_CACHED_PROFILES;i++)
459 {
460 MRUProfile[i]= (PROFILE*)HEAP_xalloc( SystemHeap, 0, sizeof(PROFILE) );
461 MRUProfile[i]->changed=FALSE;
462 MRUProfile[i]->section=NULL;
463 MRUProfile[i]->filename=NULL;
464 MRUProfile[i]->mtime=0;
465 }
466
467 /* Check for a match */
468
469 if (!strchr( filename, '/' ) &&
470 !strchr( filename, '\\' ) &&
471 !strchr( filename, ':' ))
472 {
473 GetWindowsDirectoryA( buffer, sizeof(buffer) );
474 strcat( buffer, "\\" );
475 strcat( buffer, filename );
476 }
477
478 for(i=0;i<N_CACHED_PROFILES;i++)
479 {
480 if ((MRUProfile[i]->filename && !strcmp( filename, MRUProfile[i]->filename )))
481 {
482 if(i)
483 {
484 PROFILE_FlushFile();
485 tempProfile=MRUProfile[i];
486 for(j=i;j>0;j--)
487 MRUProfile[j]=MRUProfile[j-1];
488 CurProfile=tempProfile;
489 }
490 if(!stat(CurProfile->filename,&buf) && CurProfile->mtime==buf.st_mtime)
491 dprintf(("Kernel32:Profile:(%s): already opened (mru=%d)\n",
492 filename, i ));
493 else
494 dprintf(("Kernel32:Profile:(%s): already opened, needs refreshing (mru=%d)\n",
495 filename, i ));
496 return TRUE;
497 }
498 }
499
500 /* Rotate the oldest to the top to be replaced */
501
502 if(i==N_CACHED_PROFILES)
503 {
504 tempProfile=MRUProfile[N_CACHED_PROFILES-1];
505 for(i=N_CACHED_PROFILES-1;i>0;i--)
506 MRUProfile[i]=MRUProfile[i-1];
507 CurProfile=tempProfile;
508 }
509
510 /* Flush the profile */
511
512 if(CurProfile->filename) PROFILE_ReleaseFile();
513
514 CurProfile->filename = HEAP_strdupA( SystemHeap, 0, filename );
515
516 file = fopen( filename, "r" );
517 if (file)
518 {
519 dprintf(("Kernel32:Profile:(%s): found it in %s\n",
520 filename, filename ));
521
522 CurProfile->section = PROFILE_Load( file );
523 fclose( file );
524 if(!stat(CurProfile->filename,&buf))
525 CurProfile->mtime=buf.st_mtime;
526 }
527 else
528 {
529 /* Does not exist yet, we will create it in PROFILE_FlushFile */
530 dprintf(("Kernel32:Profile:profile file %s not found\n", filename ));
531 }
532 return TRUE;
533}
534
535
536/***********************************************************************
537 * PROFILE_GetSection
538 *
539 * Returns all keys of a section.
540 * If return_values is TRUE, also include the corresponding values.
541 */
542static INT PROFILE_GetSection( PROFILESECTION *section, LPCSTR section_name,
543 LPSTR buffer, UINT len, BOOL handle_env,
544 BOOL return_values )
545{
546 PROFILEKEY *key;
547 while (section)
548 {
549 if (section->name && !strcasecmp( section->name, section_name ))
550 {
551 UINT oldlen = len;
552 for (key = section->key; key; key = key->next)
553 {
554 if (len <= 2) break;
555 if (!*key->name) continue; /* Skip empty lines */
556 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
557 PROFILE_CopyEntry( buffer, key->name, len - 1, handle_env );
558 len -= strlen(buffer) + 1;
559 buffer += strlen(buffer) + 1;
560 if (return_values && key->value) {
561 buffer[-1] = '=';
562 PROFILE_CopyEntry ( buffer,
563 key->value, len - 1, handle_env );
564 len -= strlen(buffer) + 1;
565 buffer += strlen(buffer) + 1;
566 }
567 }
568 *buffer = '\0';
569 if (len <= 1)
570 /*If either lpszSection or lpszKey is NULL and the supplied
571 destination buffer is too small to hold all the strings,
572 the last string is truncated and followed by two null characters.
573 In this case, the return value is equal to cchReturnBuffer
574 minus two. */
575 {
576 buffer[-1] = '\0';
577 return oldlen - 2;
578 }
579 return oldlen - len;
580 }
581 section = section->next;
582 }
583 buffer[0] = buffer[1] = '\0';
584 return 0;
585}
586
587
588static INT PROFILE_GetSectionNames( LPSTR buffer, UINT len )
589{
590 LPSTR buf = buffer;
591 WORD l, cursize = 0;
592 PROFILESECTION *section;
593
594 for (section = CurProfile->section; section; section = section->next)
595 if (section->name) {
596 l = strlen(section->name);
597 cursize += l+1;
598 if (cursize > len+1)
599 return len-2;
600
601 strcpy(buf, section->name);
602 buf += l+1;
603 }
604
605 *buf=0;
606 buf++;
607 return buf-buffer;
608}
609
610
611/***********************************************************************
612 * PROFILE_GetString
613 *
614 * Get a profile string.
615 */
616static INT PROFILE_GetString( LPCSTR section, LPCSTR key_name,
617 LPCSTR def_val, LPSTR buffer, UINT len )
618{
619 PROFILEKEY *key = NULL;
620
621 if (!def_val) def_val = "";
622 if (key_name && key_name[0])
623 {
624 key = PROFILE_Find( &CurProfile->section, section, key_name, FALSE );
625 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def_val,
626 len, FALSE );
627 dprintf(("Kernel32:Profile:('%s','%s','%s'): returning '%s'\n",
628 section, key_name, def_val, buffer ));
629 return strlen( buffer );
630 }
631 if (section && section[0])
632 return PROFILE_GetSection(CurProfile->section, section, buffer, len,
633 FALSE, FALSE);
634 /* undocumented; both section and key_name are NULL */
635 return PROFILE_GetSectionNames(buffer, len);
636}
637
638
639/***********************************************************************
640 * PROFILE_SetString
641 *
642 * Set a profile string.
643 */
644static BOOL PROFILE_SetString( LPCSTR section_name, LPCSTR key_name,
645 LPCSTR value )
646{
647 if (!key_name) /* Delete a whole section */
648 {
649 dprintf(("Kernel32:Profile:('%s')\n", section_name));
650 CurProfile->changed |= PROFILE_DeleteSection( &CurProfile->section,
651 section_name );
652 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
653 this is not an error on application's level.*/
654 }
655 else if (!value) /* Delete a key */
656 {
657 dprintf(("Kernel32:Profile:('%s','%s')\n",
658 section_name, key_name ));
659 CurProfile->changed |= PROFILE_DeleteKey( &CurProfile->section,
660 section_name, key_name );
661 return TRUE; /* same error handling as above */
662 }
663 else /* Set the key value */
664 {
665 PROFILEKEY *key = PROFILE_Find( &CurProfile->section, section_name,
666 key_name, TRUE );
667 dprintf(("Kernel32:Profile:('%s','%s','%s'): \n",
668 section_name, key_name, value ));
669 if (!key) return FALSE;
670 if (key->value)
671 {
672 if (!strcmp( key->value, value ))
673 {
674 dprintf(("Kernel32:Profile: no change needed\n" ));
675 return TRUE; /* No change needed */
676 }
677 dprintf(("Kernel32:Profile: replacing '%s'\n", key->value ));
678 HeapFree( SystemHeap, 0, key->value );
679 }
680 else dprintf(("Kernel32:Profile: creating key\n" ));
681 key->value = HEAP_strdupA( SystemHeap, 0, value );
682 CurProfile->changed = TRUE;
683 }
684 return TRUE;
685}
686
687
688/***********************************************************************
689 * PROFILE_GetWineIniString
690 *
691 * Get a config string from the wine.ini file.
692 */
693int PROFILE_GetWineIniString( const char *section, const char *key_name,
694 const char *def, char *buffer, int len )
695{
696 int ret;
697
698 EnterCriticalSection( &PROFILE_CritSect );
699
700 if (key_name)
701 {
702 PROFILEKEY *key = PROFILE_Find(&PROFILE_WineProfile, section, key_name, FALSE);
703 PROFILE_CopyEntry( buffer, (key && key->value) ? key->value : def,
704 len, TRUE );
705 dprintf(("Kernel32:Profile:('%s','%s','%s'): returning '%s'\n",
706 section, key_name, def, buffer ));
707 ret = strlen( buffer );
708 }
709 else
710 {
711 ret = PROFILE_GetSection( PROFILE_WineProfile, section, buffer, len, TRUE, FALSE );
712 }
713 LeaveCriticalSection( &PROFILE_CritSect );
714
715 return ret;
716}
717
718
719/***********************************************************************
720 * PROFILE_GetWineIniInt
721 *
722 * Get a config integer from the wine.ini file.
723 */
724int PROFILE_GetWineIniInt( const char *section, const char *key_name, int def )
725{
726 char buffer[20];
727 char *p;
728 long result;
729 PROFILEKEY *key;
730 int ret;
731
732 EnterCriticalSection( &PROFILE_CritSect );
733
734 key = PROFILE_Find( &PROFILE_WineProfile, section, key_name, FALSE );
735 if (!key || !key->value) {
736 ret = def;
737 } else {
738 PROFILE_CopyEntry( buffer, key->value, sizeof(buffer), TRUE );
739 result = strtol( buffer, &p, 0 );
740 ret = (p == buffer) ? 0 /* No digits at all */ : (int)result;
741 }
742
743 LeaveCriticalSection( &PROFILE_CritSect );
744
745 return ret;
746}
747
748
749/******************************************************************************
750 *
751 * int PROFILE_EnumerateWineIniSection(
752 * char const *section, #Name of the section to enumerate
753 * void (*cbfn)(char const *key, char const *value, void *user),
754 * # Address of the callback function
755 * void *user ) # User-specified pointer.
756 *
757 * For each entry in a section in the wine.conf file, this function will
758 * call the specified callback function, informing it of each key and
759 * value. An optional user pointer may be passed to it (if this is not
760 * needed, pass NULL through it and ignore the value in the callback
761 * function).
762 *
763 * The callback function must accept three parameters:
764 * The name of the key (char const *)
765 * The value of the key (char const *)
766 * A user-specified parameter (void *)
767 * Note that the first two are char CONST *'s, not char *'s! The callback
768 * MUST not modify these strings!
769 *
770 * The return value indicates the number of times the callback function
771 * was called.
772 */
773int PROFILE_EnumerateWineIniSection(
774 char const *section,
775 void (*cbfn)(char const *, char const *, void *),
776 void *userptr )
777{
778 PROFILESECTION *scansect;
779 PROFILEKEY *scankey;
780 int calls = 0;
781
782 EnterCriticalSection( &PROFILE_CritSect );
783
784 /* Search for the correct section */
785 for(scansect = PROFILE_WineProfile; scansect; scansect = scansect->next) {
786 if(scansect->name && !strcasecmp(scansect->name, section)) {
787
788 /* Enumerate each key with the callback */
789 for(scankey = scansect->key; scankey; scankey = scankey->next) {
790
791 /* Ignore blank entries -- these shouldn't exist, but let's
792 be extra careful */
793 if(scankey->name[0]) {
794 cbfn(scankey->name, scankey->value, userptr);
795 ++calls;
796 }
797 }
798
799 break;
800 }
801 }
802 LeaveCriticalSection( &PROFILE_CritSect );
803
804 return calls;
805}
806
807
808/******************************************************************************
809 *
810 * int PROFILE_GetWineIniBool(
811 * char const *section,
812 * char const *key_name,
813 * int def )
814 *
815 * Reads a boolean value from the wine.ini file. This function attempts to
816 * be user-friendly by accepting 'n', 'N' (no), 'f', 'F' (false), or '0'
817 * (zero) for false, 'y', 'Y' (yes), 't', 'T' (true), or '1' (one) for
818 * true. Anything else results in the return of the default value.
819 *
820 * This function uses 1 to indicate true, and 0 for false. You can check
821 * for existence by setting def to something other than 0 or 1 and
822 * examining the return value.
823 */
824int PROFILE_GetWineIniBool(
825 char const *section,
826 char const *key_name,
827 int def )
828{
829 char key_value[2];
830 int retval;
831
832 PROFILE_GetWineIniString(section, key_name, "~", key_value, 2);
833
834 switch(key_value[0]) {
835 case 'n':
836 case 'N':
837 case 'f':
838 case 'F':
839 case '0':
840 retval = 0;
841 break;
842
843 case 'y':
844 case 'Y':
845 case 't':
846 case 'T':
847 case '1':
848 retval = 1;
849 break;
850
851 default:
852 retval = def;
853 }
854
855 dprintf(("Kernel32:Profile:(\"%s\", \"%s\", %s), "
856 "[%c], ret %s.\n", section, key_name,
857 def ? "TRUE" : "FALSE", key_value[0],
858 retval ? "TRUE" : "FALSE"));
859
860 return retval;
861}
862
863
864#if 0
865/***********************************************************************
866 * PROFILE_LoadWineIni
867 *
868 * Load the wine.ini file.
869 */
870int PROFILE_LoadWineIni(void)
871{
872 char buffer[MAX_PATHNAME_LEN];
873 const char *p;
874 FILE *f;
875
876 InitializeCriticalSection( &PROFILE_CritSect );
877 MakeCriticalSectionGlobal( &PROFILE_CritSect );
878
879 if ( (Options.configFileName!=NULL) && (f = fopen(Options.configFileName, "r")) )
880 {
881 /* Open -config specified file */
882 PROFILE_WineProfile = PROFILE_Load ( f);
883 fclose ( f );
884 strncpy(PROFILE_WineIniUsed,Options.configFileName,MAX_PATHNAME_LEN-1);
885 return 1;
886 }
887
888 if ( (p = getenv( "WINE_INI" )) && (f = fopen( p, "r" )) )
889 {
890 PROFILE_WineProfile = PROFILE_Load( f );
891 fclose( f );
892 strncpy(PROFILE_WineIniUsed,p,MAX_PATHNAME_LEN-1);
893 return 1;
894 }
895 if ((p = getenv( "HOME" )) != NULL)
896 {
897 lstrcpynA(buffer, p, MAX_PATHNAME_LEN - sizeof(PROFILE_WineIniName));
898 strcat( buffer, PROFILE_WineIniName );
899 if ((f = fopen( buffer, "r" )) != NULL)
900 {
901 PROFILE_WineProfile = PROFILE_Load( f );
902 fclose( f );
903 strncpy(PROFILE_WineIniUsed,buffer,MAX_PATHNAME_LEN-1);
904 return 1;
905 }
906 }
907 else dprintf(("Kernel32:Profile:could not get $HOME value for config file.\n" ));
908
909 /* Try global file */
910
911 if ((f = fopen( WINE_INI_GLOBAL, "r" )) != NULL)
912 {
913 PROFILE_WineProfile = PROFILE_Load( f );
914 fclose( f );
915 strncpy(PROFILE_WineIniUsed,WINE_INI_GLOBAL,MAX_PATHNAME_LEN-1);
916 return 1;
917 }
918 MESSAGE( "Can't open configuration file %s or $HOME%s\n",
919 WINE_INI_GLOBAL, PROFILE_WineIniName );
920 return 0;
921}
922
923
924/***********************************************************************
925 * PROFILE_UsageWineIni
926 *
927 * Explain the wine.ini file to those who don't read documentation.
928 * Keep below one screenful in length so that error messages above are
929 * noticed.
930 */
931void PROFILE_UsageWineIni(void)
932{
933 MESSAGE("Perhaps you have not properly edited or created "
934 "your Wine configuration file.\n");
935 MESSAGE("This is either %s or $HOME%s\n",WINE_INI_GLOBAL,PROFILE_WineIniName);
936 MESSAGE(" or it is determined by the -config option or from\n"
937 " the WINE_INI environment variable.\n");
938 if (*PROFILE_WineIniUsed)
939 MESSAGE("Wine has used %s as configuration file.\n", PROFILE_WineIniUsed);
940 /* RTFM, so to say */
941}
942#endif
943
944/***********************************************************************
945 * PROFILE_GetStringItem
946 *
947 * Convenience function that turns a string 'xxx, yyy, zzz' into
948 * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
949 */
950char* PROFILE_GetStringItem( char* start )
951{
952 char* lpchX, *lpch;
953
954 for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++ )
955 {
956 if( *lpchX == ',' )
957 {
958 if( lpch ) *lpch = '\0'; else *lpchX = '\0';
959 while( *(++lpchX) )
960 if( !PROFILE_isspace(*lpchX) ) return lpchX;
961 }
962 else if( PROFILE_isspace( *lpchX ) && !lpch ) lpch = lpchX;
963 else lpch = NULL;
964 }
965 if( lpch ) *lpch = '\0';
966 return NULL;
967}
968
969/********************* API functions **********************************/
970
971/***********************************************************************
972 * GetProfileInt32A (KERNEL32.264)
973 */
974UINT WINAPI GetProfileIntA( LPCSTR section, LPCSTR entry, INT def_val )
975{
976 return GetPrivateProfileIntA( section, entry, def_val, "win.ini" );
977}
978
979/***********************************************************************
980 * GetProfileInt32W (KERNEL32.264)
981 */
982UINT WINAPI GetProfileIntW( LPCWSTR section, LPCWSTR entry, INT def_val )
983{
984 if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
985 return GetPrivateProfileIntW( section, entry, def_val, wininiW );
986}
987
988/***********************************************************************
989 * GetProfileString32A (KERNEL32.268)
990 */
991INT WINAPI GetProfileStringA( LPCSTR section, LPCSTR entry, LPCSTR def_val,
992 LPSTR buffer, UINT len )
993{
994 return GetPrivateProfileStringA( section, entry, def_val,
995 buffer, len, "win.ini" );
996}
997
998/***********************************************************************
999 * GetProfileString32W (KERNEL32.269)
1000 */
1001INT WINAPI GetProfileStringW( LPCWSTR section, LPCWSTR entry,
1002 LPCWSTR def_val, LPWSTR buffer, UINT len )
1003{
1004 if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1005 return GetPrivateProfileStringW( section, entry, def_val,
1006 buffer, len, wininiW );
1007}
1008
1009/***********************************************************************
1010 * WriteProfileString32A (KERNEL32.587)
1011 */
1012BOOL WINAPI WriteProfileStringA( LPCSTR section, LPCSTR entry,
1013 LPCSTR string )
1014{
1015 return WritePrivateProfileStringA( section, entry, string, "win.ini" );
1016}
1017
1018/***********************************************************************
1019 * WriteProfileString32W (KERNEL32.588)
1020 */
1021BOOL WINAPI WriteProfileStringW( LPCWSTR section, LPCWSTR entry,
1022 LPCWSTR string )
1023{
1024 if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1025 return WritePrivateProfileStringW( section, entry, string, wininiW );
1026}
1027
1028/***********************************************************************
1029 * GetPrivateProfileInt32A (KERNEL32.251)
1030 */
1031UINT WINAPI GetPrivateProfileIntA( LPCSTR section, LPCSTR entry,
1032 INT def_val, LPCSTR filename )
1033{
1034 char buffer[20];
1035 char *p;
1036 long result;
1037
1038 GetPrivateProfileStringA( section, entry, "",
1039 buffer, sizeof(buffer), filename );
1040 if (!buffer[0]) return (UINT)def_val;
1041 result = strtol( buffer, &p, 0 );
1042 if (p == buffer) return 0; /* No digits at all */
1043 return (UINT)result;
1044}
1045
1046/***********************************************************************
1047 * GetPrivateProfileInt32W (KERNEL32.252)
1048 */
1049UINT WINAPI GetPrivateProfileIntW( LPCWSTR section, LPCWSTR entry,
1050 INT def_val, LPCWSTR filename )
1051{
1052 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1053 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1054 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1055 UINT res = GetPrivateProfileIntA(sectionA, entryA, def_val, filenameA);
1056 HeapFree( GetProcessHeap(), 0, sectionA );
1057 HeapFree( GetProcessHeap(), 0, filenameA );
1058 HeapFree( GetProcessHeap(), 0, entryA );
1059 return res;
1060}
1061
1062/***********************************************************************
1063 * GetPrivateProfileString32A (KERNEL32.255)
1064 */
1065INT WINAPI GetPrivateProfileStringA( LPCSTR section, LPCSTR entry,
1066 LPCSTR def_val, LPSTR buffer,
1067 UINT len, LPCSTR filename )
1068{
1069 int ret;
1070
1071 if (!filename)
1072 filename = "win.ini";
1073
1074 EnterCriticalSection( &PROFILE_CritSect );
1075
1076 if (PROFILE_Open( filename )) {
1077 ret = PROFILE_GetString( section, entry, def_val, buffer, len );
1078 } else {
1079 lstrcpynA( buffer, def_val, len );
1080 ret = strlen( buffer );
1081 }
1082
1083 LeaveCriticalSection( &PROFILE_CritSect );
1084
1085 return ret;
1086}
1087
1088/***********************************************************************
1089 * GetPrivateProfileString32W (KERNEL32.256)
1090 */
1091INT WINAPI GetPrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1092 LPCWSTR def_val, LPWSTR buffer,
1093 UINT len, LPCWSTR filename )
1094{
1095 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1096 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1097 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1098 LPSTR def_valA = HEAP_strdupWtoA( GetProcessHeap(), 0, def_val );
1099 LPSTR bufferA = (LPSTR)HeapAlloc( GetProcessHeap(), 0, len );
1100 INT ret = GetPrivateProfileStringA( sectionA, entryA, def_valA,
1101 bufferA, len, filenameA );
1102 lstrcpynAtoW( buffer, bufferA, len );
1103 HeapFree( GetProcessHeap(), 0, sectionA );
1104 HeapFree( GetProcessHeap(), 0, entryA );
1105 HeapFree( GetProcessHeap(), 0, filenameA );
1106 HeapFree( GetProcessHeap(), 0, def_valA );
1107 HeapFree( GetProcessHeap(), 0, bufferA);
1108 return ret;
1109}
1110
1111/***********************************************************************
1112 * GetPrivateProfileSection32A (KERNEL32.255)
1113 */
1114INT WINAPI GetPrivateProfileSectionA( LPCSTR section, LPSTR buffer,
1115 DWORD len, LPCSTR filename )
1116{
1117 int ret = 0;
1118
1119 EnterCriticalSection( &PROFILE_CritSect );
1120
1121 if (PROFILE_Open( filename ))
1122 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1123 FALSE, TRUE);
1124
1125 LeaveCriticalSection( &PROFILE_CritSect );
1126
1127 return ret;
1128}
1129
1130/***********************************************************************
1131 * GetPrivateProfileSection32W (KERNEL32.256)
1132 */
1133
1134INT WINAPI GetPrivateProfileSectionW (LPCWSTR section, LPWSTR buffer,
1135 DWORD len, LPCWSTR filename )
1136
1137{
1138 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1139 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1140 LPSTR bufferA = (LPSTR)HeapAlloc( GetProcessHeap(), 0, len );
1141 INT ret = GetPrivateProfileSectionA( sectionA, bufferA, len,
1142 filenameA );
1143 MultiByteToWideChar(CP_ACP,0,bufferA,ret,buffer,len);
1144 HeapFree( GetProcessHeap(), 0, sectionA );
1145 HeapFree( GetProcessHeap(), 0, filenameA );
1146 HeapFree( GetProcessHeap(), 0, bufferA);
1147 return ret;
1148}
1149
1150/***********************************************************************
1151 * GetProfileSection32A (KERNEL32.268)
1152 */
1153INT WINAPI GetProfileSectionA( LPCSTR section, LPSTR buffer, DWORD len )
1154{
1155 return GetPrivateProfileSectionA( section, buffer, len, "win.ini" );
1156}
1157
1158/***********************************************************************
1159 * GetProfileSection32W (KERNEL32)
1160 */
1161INT WINAPI GetProfileSectionW( LPCWSTR section, LPWSTR buffer, DWORD len )
1162{
1163 if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini" );
1164 return GetPrivateProfileSectionW( section, buffer, len, wininiW );
1165}
1166
1167/***********************************************************************
1168 * WritePrivateProfileString32A (KERNEL32.582)
1169 */
1170BOOL WINAPI WritePrivateProfileStringA( LPCSTR section, LPCSTR entry,
1171 LPCSTR string, LPCSTR filename )
1172{
1173 BOOL ret = FALSE;
1174
1175 EnterCriticalSection( &PROFILE_CritSect );
1176
1177 if (PROFILE_Open( filename ))
1178 {
1179 if (!section && !entry && !string)
1180 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1181 else
1182 ret = PROFILE_SetString( section, entry, string );
1183 }
1184
1185 LeaveCriticalSection( &PROFILE_CritSect );
1186 return ret;
1187}
1188
1189/***********************************************************************
1190 * WritePrivateProfileString32W (KERNEL32.583)
1191 */
1192BOOL WINAPI WritePrivateProfileStringW( LPCWSTR section, LPCWSTR entry,
1193 LPCWSTR string, LPCWSTR filename )
1194{
1195 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1196 LPSTR entryA = HEAP_strdupWtoA( GetProcessHeap(), 0, entry );
1197 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1198 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1199 BOOL res = WritePrivateProfileStringA( sectionA, entryA,
1200 stringA, filenameA );
1201 HeapFree( GetProcessHeap(), 0, sectionA );
1202 HeapFree( GetProcessHeap(), 0, entryA );
1203 HeapFree( GetProcessHeap(), 0, stringA );
1204 HeapFree( GetProcessHeap(), 0, filenameA );
1205 return res;
1206}
1207
1208/***********************************************************************
1209 * WritePrivateProfileSection32A (KERNEL32)
1210 */
1211BOOL WINAPI WritePrivateProfileSectionA( LPCSTR section,
1212 LPCSTR string, LPCSTR filename )
1213{
1214 char *p =(char*)string;
1215
1216 dprintf(("Kernel32:Profile:fixme WritePrivateProfileSection32A empty stub\n"));
1217 return FALSE;
1218}
1219
1220/***********************************************************************
1221 * WritePrivateProfileSection32W (KERNEL32)
1222 */
1223BOOL WINAPI WritePrivateProfileSectionW( LPCWSTR section,
1224 LPCWSTR string, LPCWSTR filename)
1225
1226{
1227 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1228 LPSTR stringA = HEAP_strdupWtoA( GetProcessHeap(), 0, string );
1229 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1230 BOOL res = WritePrivateProfileSectionA( sectionA, stringA, filenameA );
1231 HeapFree( GetProcessHeap(), 0, sectionA );
1232 HeapFree( GetProcessHeap(), 0, stringA );
1233 HeapFree( GetProcessHeap(), 0, filenameA );
1234 return res;
1235}
1236
1237/***********************************************************************
1238 * WriteProfileSection32A (KERNEL32.747)
1239 */
1240BOOL WINAPI WriteProfileSectionA( LPCSTR section, LPCSTR keys_n_values)
1241
1242{
1243 return WritePrivateProfileSectionA( section, keys_n_values, "win.ini");
1244}
1245
1246/***********************************************************************
1247 * WriteProfileSection32W (KERNEL32.748)
1248 */
1249BOOL WINAPI WriteProfileSectionW( LPCWSTR section, LPCWSTR keys_n_values)
1250{
1251 if (!wininiW) wininiW = HEAP_strdupAtoW( SystemHeap, 0, "win.ini");
1252
1253 return (WritePrivateProfileSectionW (section,keys_n_values, wininiW));
1254}
1255
1256/***********************************************************************
1257 * GetPrivateProfileSectionNames16 (KERNEL.143)
1258 */
1259WORD WINAPI GetPrivateProfileSectionNames16( LPSTR buffer, WORD size,
1260 LPCSTR filename )
1261{
1262 WORD ret = 0;
1263
1264 EnterCriticalSection( &PROFILE_CritSect );
1265
1266 if (PROFILE_Open( filename ))
1267 ret = PROFILE_GetSectionNames(buffer, size);
1268
1269 LeaveCriticalSection( &PROFILE_CritSect );
1270
1271 return ret;
1272}
1273
1274/***********************************************************************
1275 * GetPrivateProfileSectionNames32A (KERNEL32.365)
1276 */
1277DWORD WINAPI GetPrivateProfileSectionNamesA( LPSTR buffer, DWORD size,
1278 LPCSTR filename)
1279
1280{
1281 return (GetPrivateProfileSectionNames16 (buffer,size,filename));
1282}
1283
1284/***********************************************************************
1285 * GetPrivateProfileSectionNames32W (KERNEL32.366)
1286 */
1287DWORD WINAPI GetPrivateProfileSectionNamesW( LPWSTR buffer, DWORD size,
1288 LPCWSTR filename)
1289
1290{
1291 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1292 LPSTR bufferA = (LPSTR)HeapAlloc( GetProcessHeap(), 0, size);
1293
1294 INT ret = GetPrivateProfileSectionNames16 (bufferA, size, filenameA);
1295 lstrcpynAtoW( buffer, bufferA, size);
1296 HeapFree( GetProcessHeap(), 0, bufferA);
1297 HeapFree( GetProcessHeap(), 0, filenameA );
1298
1299 return ret;
1300}
1301
1302/***********************************************************************
1303 * GetPrivateProfileStruct32A (KERNEL32.370)
1304 */
1305BOOL WINAPI GetPrivateProfileStructA (LPCSTR section, LPCSTR key,
1306 LPVOID buf, UINT len, LPCSTR filename)
1307{
1308 BOOL ret = FALSE;
1309
1310 EnterCriticalSection( &PROFILE_CritSect );
1311
1312 if (PROFILE_Open( filename )) {
1313 PROFILEKEY *k = PROFILE_Find ( &CurProfile->section, section, key, FALSE);
1314 if (k) {
1315 lstrcpynA( buf, k->value, strlen(k->value));
1316 ret = TRUE;
1317 }
1318 }
1319 LeaveCriticalSection( &PROFILE_CritSect );
1320
1321 return FALSE;
1322}
1323
1324/***********************************************************************
1325 * GetPrivateProfileStruct32W (KERNEL32.543)
1326 */
1327BOOL WINAPI GetPrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1328 LPVOID buffer, UINT len, LPCWSTR filename)
1329{
1330 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1331 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1332 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1333 LPSTR bufferA = (LPSTR)HeapAlloc( GetProcessHeap(), 0, len );
1334
1335 INT ret = GetPrivateProfileStructA( sectionA, keyA, bufferA,
1336 len, filenameA );
1337 lstrcpynAtoW( buffer, bufferA, len );
1338 HeapFree( GetProcessHeap(), 0, bufferA);
1339 HeapFree( GetProcessHeap(), 0, sectionA );
1340 HeapFree( GetProcessHeap(), 0, keyA );
1341 HeapFree( GetProcessHeap(), 0, filenameA );
1342
1343 return ret;
1344}
1345
1346
1347/***********************************************************************
1348 * WritePrivateProfileStruct32A (KERNEL32.744)
1349 */
1350BOOL WINAPI WritePrivateProfileStructA (LPCSTR section, LPCSTR key,
1351 LPVOID buf, UINT bufsize, LPCSTR filename)
1352{
1353 BOOL ret = FALSE;
1354
1355 if (!section && !key && !buf) /* flush the cache */
1356 return WritePrivateProfileStringA( NULL, NULL, NULL, filename );
1357
1358 EnterCriticalSection( &PROFILE_CritSect );
1359
1360 if (PROFILE_Open( filename ))
1361 ret = PROFILE_SetString( section, key, (LPCSTR)buf );
1362
1363 LeaveCriticalSection( &PROFILE_CritSect );
1364
1365 return ret;
1366}
1367
1368/***********************************************************************
1369 * WritePrivateProfileStruct32W (KERNEL32.544)
1370 */
1371BOOL WINAPI WritePrivateProfileStructW (LPCWSTR section, LPCWSTR key,
1372 LPVOID buf, UINT bufsize, LPCWSTR filename)
1373{
1374 LPSTR sectionA = HEAP_strdupWtoA( GetProcessHeap(), 0, section );
1375 LPSTR keyA = HEAP_strdupWtoA( GetProcessHeap(), 0, key);
1376 LPSTR filenameA = HEAP_strdupWtoA( GetProcessHeap(), 0, filename );
1377 INT ret = WritePrivateProfileStructA( sectionA, keyA, buf, bufsize,
1378 filenameA );
1379 HeapFree( GetProcessHeap(), 0, sectionA );
1380 HeapFree( GetProcessHeap(), 0, keyA );
1381 HeapFree( GetProcessHeap(), 0, filenameA );
1382
1383 return ret;
1384}
Note: See TracBrowser for help on using the repository browser.