1 /*
2 * Profile functions
3 *
4 * Copyright 1993 Miguel de Icaza
5 * Copyright 1996 Alexandre Julliard
6 *
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.
11 *
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.
16 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
20 */
21 #include "my_global.h"
22
23 #include <ctype.h>
24 //#include <errno.h>
25 #include <fcntl.h>
26 //#include <io.h> commented this line out to compile for solaris
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 //#include <sys/types.h>
32 //#include <memory.h>
33 #include "osutil.h"
34 #include "global.h"
35 #include "inihandl.h"
36
37 // The types and variables used locally
38 //typedef int bool;
39 typedef unsigned int uint;
40 //#define SVP(S) ((S) ? S : "<null>")
41 #define _strlwr(P) strlwr(P) //OB: changed this line
42 #define MAX_PATHNAME_LEN 256
43 #define N_CACHED_PROFILES 10
44 #ifndef WIN32
45 #define stricmp strcasecmp
46 #define _strnicmp strncasecmp
47 #endif // !WIN32
48 #define EnterCriticalSection(x)
49 #define LeaveCriticalSection(x)
50
51 #if defined(TEST_MODULE)
52 // Stand alone test program
53 #include <stdarg.h>
54 int trace = 0;
htrc(char const * fmt,...)55 void htrc(char const *fmt, ...)
56 {
57 va_list ap;
58 va_start (ap, fmt);
59 vfprintf(stderr, fmt, ap);
60 va_end (ap);
61 } /* end of htrc */
62 #else // !TEST_MODULE
63 // Normal included functions
64 //extern int trace;
65 //void htrc(char const *fmt, ...);
66 #endif // !TEST MODULE
67
68
69 typedef struct tagPROFILEKEY {
70 char *value;
71 struct tagPROFILEKEY *next;
72 char name[1];
73 } PROFILEKEY;
74
75 typedef struct tagPROFILESECTION {
76 struct tagPROFILEKEY *key;
77 struct tagPROFILESECTION *next;
78 char name[1];
79 } PROFILESECTION;
80
81 typedef struct {
82 BOOL changed;
83 PROFILESECTION *section;
84 //char *dos_name;
85 //char *unix_name;
86 char *filename;
87 time_t mtime;
88 } PROFILE;
89
90 #define memfree(P) if (P) free(P)
91
92 /* Cached profile files */
93 static PROFILE *MRUProfile[N_CACHED_PROFILES] = {NULL};
94
95 #define CurProfile (MRUProfile[0])
96
97 /* wine.ini config file registry root */
98 //static HKEY wine_profile_key;
99
100 #define PROFILE_MAX_LINE_LEN 1024
101
102 /* Wine profile name in $HOME directory; must begin with slash */
103 //static const char PROFILE_WineIniName[] = "/.winerc";
104
105 /* Wine profile: the profile file being used */
106 //static char PROFILE_WineIniUsed[MAX_PATHNAME_LEN] = "";
107
108 /* Check for comments in profile */
109 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
110
111 //static const WCHAR wininiW[] = { 'w','i','n','.','i','n','i',0 };
112
113 //static CRITICAL_SECTION PROFILE_CritSect = CRITICAL_SECTION_INIT("PROFILE_CritSect");
114
115 BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry,
116 LPCSTR string, LPCSTR filename);
117
118 /***********************************************************************
119 * PROFILE_CopyEntry
120 *
121 * Copy the content of an entry into a buffer, removing quotes,
122 * and possibly translating environment variables.
123 ***********************************************************************/
PROFILE_CopyEntry(char * buffer,const char * value,uint len,int handle_env)124 static void PROFILE_CopyEntry( char *buffer, const char *value, uint len,
125 int handle_env )
126 {
127 const char *p;
128 char quote = '\0';
129
130 if (!buffer)
131 return;
132
133 if ((*value == '\'') || (*value == '\"'))
134 if (value[1] && (value[strlen(value)-1] == *value))
135 quote = *value++;
136
137 if (!handle_env) {
138 strncpy(buffer, value, len);
139
140 if (quote && (len >= strlen(value)))
141 buffer[strlen(buffer)-1] = '\0';
142
143 return;
144 } // endif handle
145
146 for (p = value; (*p && (len > 1)); *buffer++ = *p++, len--) {
147 if ((*p == '$') && (p[1] == '{')) {
148 char env_val[1024];
149 const char *env_p;
150 const char *p2 = strchr(p, '}');
151
152 if (!p2)
153 continue; /* ignore it */
154
155 strncpy(env_val, p + 2, MY_MIN((int) sizeof(env_val), (int)(p2-p)-1));
156
157 if ((env_p = getenv(env_val)) != NULL) {
158 int buffer_len;
159
160 strncpy( buffer, env_p, len );
161 buffer_len = strlen( buffer );
162 buffer += buffer_len;
163 len -= buffer_len;
164 } // endif env_p
165
166 p = p2 + 1;
167 } // endif p
168
169 } // endfor p
170
171 if (quote && (len > 1))
172 buffer--;
173
174 *buffer = '\0';
175 } // end of PROFILE_CopyEntry
176
177
178 /***********************************************************************
179 * PROFILE_Save
180 *
181 * Save a profile tree to a file.
182 ***********************************************************************/
PROFILE_Save(FILE * file,PROFILESECTION * section)183 static void PROFILE_Save( FILE *file, PROFILESECTION *section )
184 {
185 PROFILEKEY *key;
186 int secno;
187
188 for (secno= 0; section; section= section->next) {
189 if (section->name[0]) {
190 fprintf(file, "%s[%s]\n", secno ? "\n" : "", SVP(section->name));
191 secno++;
192 }
193
194 for (key = section->key; key; key = key->next) {
195 if (key->name[0]) {
196 fprintf(file, "%s", SVP(key->name));
197
198 if (key->value)
199 fprintf(file, "=%s", SVP(key->value));
200
201 fprintf(file, "\n");
202 } // endif key->name
203 }
204 } // endfor section
205
206 } // end of PROFILE_Save
207
208
209 /***********************************************************************
210 * PROFILE_Free
211 *
212 * Free a profile tree.
213 ***********************************************************************/
PROFILE_Free(PROFILESECTION * section)214 static void PROFILE_Free( PROFILESECTION *section )
215 {
216 PROFILESECTION *next_section;
217 PROFILEKEY *key, *next_key;
218
219 for (; section; section = next_section) {
220 for (key = section->key; key; key = next_key) {
221 next_key = key->next;
222 memfree(key->value);
223 free(key);
224 } // endfor key
225
226 next_section = section->next;
227 free(section);
228 } // endfor section
229
230 } // end of PROFILE_Free
231
PROFILE_isspace(char c)232 static int PROFILE_isspace(char c)
233 {
234 /* CR and ^Z (DOS EOF) are spaces too (found on CD-ROMs) */
235 if (isspace(c) || c=='\r' || c==0x1a)
236 return 1;
237
238 return 0;
239 } // end of PROFILE_isspace
240
241
242 /***********************************************************************
243 * PROFILE_Load
244 *
245 * Load a profile tree from a file.
246 ***********************************************************************/
PROFILE_Load(FILE * file)247 static PROFILESECTION *PROFILE_Load( FILE *file )
248 {
249 char buffer[PROFILE_MAX_LINE_LEN];
250 char *p, *p2;
251 int line = 0;
252 PROFILESECTION *section, *first_section;
253 PROFILESECTION* *next_section;
254 PROFILEKEY *key, *prev_key, **next_key;
255
256 first_section = (PROFILESECTION*)malloc(sizeof(*section));
257
258 if (first_section == NULL)
259 return NULL;
260
261 first_section->name[0] = 0;
262 first_section->key = NULL;
263 first_section->next = NULL;
264 next_section = &first_section->next;
265 next_key = &first_section->key;
266 prev_key = NULL;
267
268 while (fgets(buffer, PROFILE_MAX_LINE_LEN, file)) {
269 line++;
270 p = buffer;
271
272 while (*p && PROFILE_isspace(*p))
273 p++;
274
275 if (*p == '[') { /* section start */
276 if (!(p2 = strrchr( p, ']'))) {
277 fprintf(stderr, "Invalid section header at line %d: '%s'\n",
278 line, p);
279 } else {
280 *p2 = '\0';
281 p++;
282
283 if (!(section = (PROFILESECTION*)malloc(sizeof(*section) + strlen(p))))
284 break;
285
286 strcpy(section->name, p);
287 section->key = NULL;
288 section->next = NULL;
289 *next_section = section;
290 next_section = §ion->next;
291 next_key = §ion->key;
292 prev_key = NULL;
293
294 if (trace(2))
295 htrc("New section: '%s'\n",section->name);
296
297 continue;
298 } // endif p2
299
300 } // endif p
301
302 p2 = p + strlen(p) - 1;
303
304 while ((p2 > p) && ((*p2 == '\n') || PROFILE_isspace(*p2)))
305 *p2-- = '\0';
306
307 if ((p2 = strchr(p, '=')) != NULL) {
308 char *p3 = p2 - 1;
309
310 while ((p3 > p) && PROFILE_isspace(*p3))
311 *p3-- = '\0';
312
313 *p2++ = '\0';
314
315 while (*p2 && PROFILE_isspace(*p2))
316 p2++;
317
318 } // endif p2
319
320 if (*p || !prev_key || *prev_key->name) {
321 if (!(key = (PROFILEKEY*)malloc(sizeof(*key) + strlen(p))))
322 break;
323
324 strcpy(key->name, p);
325
326 if (p2) {
327 key->value = (char*)malloc(strlen(p2)+1);
328 strcpy(key->value, p2);
329 } else
330 key->value = NULL;
331
332 key->next = NULL;
333 *next_key = key;
334 next_key = &key->next;
335 prev_key = key;
336
337 if (trace(2))
338 htrc("New key: name='%s', value='%s'\n",
339 key->name,key->value?key->value:"(none)");
340
341 } // endif p || prev_key
342
343 } // endif *p
344
345 return first_section;
346 } // end of PROFILE_Load
347
348 /***********************************************************************
349 * PROFILE_FlushFile
350 *
351 * Flush the current profile to disk if changed.
352 ***********************************************************************/
PROFILE_FlushFile(void)353 static BOOL PROFILE_FlushFile(void)
354 {
355 //char *p, buffer[MAX_PATHNAME_LEN];
356 //const char *unix_name;
357 FILE *file = NULL;
358 struct stat buf;
359
360 if (trace(2))
361 htrc("PROFILE_FlushFile: CurProfile=%p\n", CurProfile);
362
363 if (!CurProfile) {
364 fprintf(stderr, "No current profile!\n");
365 return FALSE;
366 } // endif !CurProfile
367
368 if (!CurProfile->changed || !CurProfile->filename)
369 return TRUE;
370
371 #if 0
372 if (!(file = fopen(unix_name, "w"))) {
373 /* Try to create it in $HOME/.wine */
374 /* FIXME: this will need a more general solution */
375 //strcpy( buffer, get_config_dir() );
376 //p = buffer + strlen(buffer);
377 //*p++ = '/';
378 char *p1 = strrchr(CurProfile->filename, '\\');
379
380 p = buffer; // OB: To be elaborate
381
382 if (p1)
383 p1++;
384 else
385 p1 = CurProfile->dos_name;
386
387 strcpy(p, p1);
388 _strlwr(p);
389 file = fopen(buffer, "w");
390 unix_name = buffer;
391 } // endif !unix_name
392 #endif // 0
393
394 if (!(file = fopen(CurProfile->filename, "w"))) {
395 fprintf(stderr, "could not save profile file %s\n", CurProfile->filename);
396 return FALSE;
397 } // endif !file
398
399 if (trace(2))
400 htrc("Saving '%s'\n", CurProfile->filename);
401
402 PROFILE_Save(file, CurProfile->section);
403 fclose(file);
404 CurProfile->changed = FALSE;
405
406 if (!stat(CurProfile->filename, &buf))
407 CurProfile->mtime = buf.st_mtime;
408
409 return TRUE;
410 } // end of PROFILE_FlushFile
411
412
413 /***********************************************************************
414 * PROFILE_ReleaseFile
415 *
416 * Flush the current profile to disk and remove it from the cache.
417 ***********************************************************************/
PROFILE_ReleaseFile(void)418 static void PROFILE_ReleaseFile(void)
419 {
420 PROFILE_FlushFile();
421 PROFILE_Free(CurProfile->section);
422 //memfree(CurProfile->dos_name);
423 //memfree(CurProfile->unix_name);
424 memfree(CurProfile->filename);
425 CurProfile->changed = FALSE;
426 CurProfile->section = NULL;
427 //CurProfile->dos_name = NULL;
428 //CurProfile->unix_name = NULL;
429 CurProfile->filename = NULL;
430 CurProfile->mtime = 0;
431 } // end of PROFILE_ReleaseFile
432
433
434 /***********************************************************************
435 * PROFILE_Open
436 *
437 * Open a profile file, checking the cached file first.
438 ***********************************************************************/
PROFILE_Open(LPCSTR filename)439 static BOOL PROFILE_Open(LPCSTR filename)
440 {
441 //char buffer[MAX_PATHNAME_LEN];
442 //char *p;
443 FILE *file = NULL;
444 int i, j;
445 struct stat buf;
446 PROFILE *tempProfile;
447
448 if (trace(2))
449 htrc("PROFILE_Open: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
450
451 /* First time around */
452 if (!CurProfile)
453 for (i = 0; i < N_CACHED_PROFILES; i++) {
454 MRUProfile[i] = (PROFILE*)malloc(sizeof(PROFILE));
455
456 if (MRUProfile[i] == NULL)
457 break;
458
459 MRUProfile[i]->changed=FALSE;
460 MRUProfile[i]->section=NULL;
461 // MRUProfile[i]->dos_name=NULL;
462 // MRUProfile[i]->unix_name=NULL;
463 MRUProfile[i]->filename=NULL;
464 MRUProfile[i]->mtime=0;
465 } // endfor i
466
467 /* Check for a match */
468 for (i = 0; i < N_CACHED_PROFILES; i++) {
469 if (trace(2))
470 htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
471
472 if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) {
473 if (i) {
474 PROFILE_FlushFile();
475 tempProfile = MRUProfile[i];
476
477 for (j = i; j > 0; j--)
478 MRUProfile[j] = MRUProfile[j-1];
479
480 CurProfile=tempProfile;
481 } // endif i
482
483 if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime) {
484 if (trace(2))
485 htrc("(%s): already opened (mru=%d)\n", filename, i);
486
487 } else {
488 if (trace(2))
489 htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i);
490
491 } // endif stat
492
493 return TRUE;
494 } // endif filename
495
496 } // endfor i
497
498 /* Flush the old current profile */
499 PROFILE_FlushFile();
500
501 /* Make the oldest profile the current one only in order to get rid of it */
502 if (i == N_CACHED_PROFILES) {
503 tempProfile = MRUProfile[N_CACHED_PROFILES-1];
504
505 for(i = N_CACHED_PROFILES-1; i > 0; i--)
506 MRUProfile[i] = MRUProfile[i-1];
507
508 CurProfile = tempProfile;
509 } // endif i
510
511 if (CurProfile->filename)
512 PROFILE_ReleaseFile();
513
514 /* OK, now that CurProfile is definitely free we assign it our new file */
515 // newdos_name = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.short_name)+1 );
516 // strcpy( newdos_name, full_name.short_name );
517
518 // newdos_name = malloc(strlen(filename)+1);
519 // strcpy(newdos_name, filename);
520
521 // CurProfile->dos_name = newdos_name;
522 CurProfile->filename = (char*)malloc(strlen(filename) + 1);
523 strcpy(CurProfile->filename, filename);
524
525 /* Try to open the profile file, first in $HOME/.wine */
526
527 /* FIXME: this will need a more general solution */
528 // strcpy( buffer, get_config_dir() );
529 // p = buffer + strlen(buffer);
530 // *p++ = '/';
531 // strcpy( p, strrchr( newdos_name, '\\' ) + 1 );
532 // p = buffer;
533 // strcpy(p, filename);
534 // _strlwr(p);
535
536 if (trace(2))
537 htrc("Opening %s\n", filename);
538
539 if ((file = fopen(filename, "r"))) {
540 if (trace(2))
541 htrc("(%s): found it\n", filename);
542
543 // CurProfile->unix_name = malloc(strlen(buffer)+1);
544 // strcpy(CurProfile->unix_name, buffer);
545 } /* endif file */
546
547 if (file) {
548 CurProfile->section = PROFILE_Load(file);
549 fclose(file);
550
551 if (!stat(CurProfile->filename, &buf))
552 CurProfile->mtime = buf.st_mtime;
553
554 } else {
555 /* Does not exist yet, we will create it in PROFILE_FlushFile */
556 fprintf(stderr, "profile file %s not found\n", filename);
557 } /* endif file */
558
559 return TRUE;
560 }
561
562
563 /***********************************************************************
564 * PROFILE_Close
565 *
566 * Flush the named profile to disk and remove it from the cache.
567 ***********************************************************************/
PROFILE_Close(LPCSTR filename)568 void PROFILE_Close(LPCSTR filename)
569 {
570 int i;
571 BOOL close = FALSE;
572 struct stat buf;
573 PROFILE *tempProfile;
574
575 if (trace(2))
576 htrc("PROFILE_Close: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
577
578 /* Check for a match */
579 for (i = 0; i < N_CACHED_PROFILES; i++) {
580 if (trace(2))
581 htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
582
583 if (MRUProfile[i]->filename && !strcmp(filename, MRUProfile[i]->filename)) {
584 if (i) {
585 /* Make the profile to close current */
586 tempProfile = MRUProfile[i];
587 MRUProfile[i] = MRUProfile[0];
588 MRUProfile[0] = tempProfile;
589 CurProfile=tempProfile;
590 } // endif i
591
592 if (trace(2)) {
593 if (!stat(CurProfile->filename, &buf) && CurProfile->mtime == buf.st_mtime)
594 htrc("(%s): already opened (mru=%d)\n", filename, i);
595 else
596 htrc("(%s): already opened, needs refreshing (mru=%d)\n", filename, i);
597
598 } // endif trace
599
600 close = TRUE;
601 break;
602 } // endif filename
603
604 } // endfor i
605
606 if (close)
607 PROFILE_ReleaseFile();
608
609 } // end of PROFILE_Close
610
611
612 /***********************************************************************
613 * PROFILE_End
614 *
615 * Terminate and release the cache.
616 ***********************************************************************/
PROFILE_End(void)617 void PROFILE_End(void)
618 {
619 int i;
620
621 if (trace(3))
622 htrc("PROFILE_End: CurProfile=%p N=%d\n", CurProfile, N_CACHED_PROFILES);
623
624 if (!CurProfile) // Sergey Vojtovich
625 return;
626
627 /* Close all opened files and free the cache structure */
628 for (i = 0; i < N_CACHED_PROFILES; i++) {
629 if (trace(3))
630 htrc("MRU=%s i=%d\n", SVP(MRUProfile[i]->filename), i);
631
632 // CurProfile = MRUProfile[i]; Sergey Vojtovich
633 // PROFILE_ReleaseFile(); see MDEV-9997
634 free(MRUProfile[i]);
635 } // endfor i
636
637 } // end of PROFILE_End
638
639
640 /***********************************************************************
641 * PROFILE_DeleteSection
642 *
643 * Delete a section from a profile tree.
644 ***********************************************************************/
PROFILE_DeleteSection(PROFILESECTION ** section,LPCSTR name)645 static BOOL PROFILE_DeleteSection(PROFILESECTION* *section, LPCSTR name)
646 {
647 while (*section) {
648 if ((*section)->name[0] && !stricmp((*section)->name, name)) {
649 PROFILESECTION *to_del = *section;
650
651 *section = to_del->next;
652 to_del->next = NULL;
653 PROFILE_Free(to_del);
654 return TRUE;
655 } // endif section
656
657 section = &(*section)->next;
658 } // endwhile section
659
660 return FALSE;
661 } // end of PROFILE_DeleteSection
662
663
664 /***********************************************************************
665 * PROFILE_DeleteKey
666 *
667 * Delete a key from a profile tree.
668 ***********************************************************************/
PROFILE_DeleteKey(PROFILESECTION ** section,LPCSTR section_name,LPCSTR key_name)669 static BOOL PROFILE_DeleteKey(PROFILESECTION* *section,
670 LPCSTR section_name, LPCSTR key_name)
671 {
672 while (*section) {
673 if ((*section)->name[0] && !stricmp((*section)->name, section_name)) {
674 PROFILEKEY* *key = &(*section)->key;
675
676 while (*key) {
677 if (!stricmp((*key)->name, key_name)) {
678 PROFILEKEY *to_del = *key;
679
680 *key = to_del->next;
681 memfree(to_del->value);
682 free(to_del);
683 return TRUE;
684 } // endif name
685
686 key = &(*key)->next;
687 } // endwhile *key
688
689 } // endif section->name
690
691 section = &(*section)->next;
692 } // endwhile *section
693
694 return FALSE;
695 } // end of PROFILE_DeleteKey
696
697
698 /***********************************************************************
699 * PROFILE_DeleteAllKeys
700 *
701 * Delete all keys from a profile tree.
702 ***********************************************************************/
PROFILE_DeleteAllKeys(LPCSTR section_name)703 static void PROFILE_DeleteAllKeys(LPCSTR section_name)
704 {
705 PROFILESECTION* *section= &CurProfile->section;
706
707 while (*section) {
708 if ((*section)->name[0] && !stricmp((*section)->name, section_name)) {
709 PROFILEKEY* *key = &(*section)->key;
710
711 while (*key) {
712 PROFILEKEY *to_del = *key;
713
714 *key = to_del->next;
715 memfree(to_del->value);
716 free(to_del);
717 CurProfile->changed = TRUE;
718 } // endwhile *key
719
720 } // endif section->name
721
722 section = &(*section)->next;
723 } // endwhile *section
724
725 } // end of PROFILE_DeleteAllKeys
726
727
728 /***********************************************************************
729 * PROFILE_Find
730 *
731 * Find a key in a profile tree, optionally creating it.
732 ***********************************************************************/
PROFILE_Find(PROFILESECTION ** section,const char * section_name,const char * key_name,BOOL create,BOOL create_always)733 static PROFILEKEY *PROFILE_Find(PROFILESECTION* *section,
734 const char *section_name,
735 const char *key_name,
736 BOOL create, BOOL create_always)
737 {
738 const char *p;
739 int seclen, keylen;
740
741 while (PROFILE_isspace(*section_name))
742 section_name++;
743
744 p = section_name + strlen(section_name) - 1;
745
746 while ((p > section_name) && PROFILE_isspace(*p))
747 p--;
748
749 seclen = p - section_name + 1;
750
751 while (PROFILE_isspace(*key_name))
752 key_name++;
753
754 p = key_name + strlen(key_name) - 1;
755
756 while ((p > key_name) && PROFILE_isspace(*p))
757 p--;
758
759 keylen = p - key_name + 1;
760
761 while (*section) {
762 if (((*section)->name[0])
763 && (!(_strnicmp((*section)->name, section_name, seclen )))
764 && (((*section)->name)[seclen] == '\0')) {
765 PROFILEKEY* *key = &(*section)->key;
766
767 while (*key) {
768 /* If create_always is FALSE then we check if the keyname already exists.
769 * Otherwise we add it regardless of its existence, to allow
770 * keys to be added more then once in some cases.
771 */
772 if (!create_always) {
773 if ((!(_strnicmp( (*key)->name, key_name, keylen )))
774 && (((*key)->name)[keylen] == '\0'))
775 return *key;
776
777 } // endif !create_always
778
779 key = &(*key)->next;
780 } // endwhile *key
781
782 if (!create)
783 return NULL;
784
785 if (!(*key = (PROFILEKEY*)malloc(sizeof(PROFILEKEY) + strlen(key_name))))
786 return NULL;
787
788 strcpy((*key)->name, key_name);
789 (*key)->value = NULL;
790 (*key)->next = NULL;
791 return *key;
792 } // endifsection->name
793
794 section = &(*section)->next;
795 } // endwhile *section
796
797 if (!create)
798 return NULL;
799
800 *section = (PROFILESECTION*)malloc(sizeof(PROFILESECTION) + strlen(section_name));
801
802 if (*section == NULL)
803 return NULL;
804
805 strcpy((*section)->name, section_name);
806 (*section)->next = NULL;
807
808 if (!((*section)->key = (tagPROFILEKEY*)malloc(sizeof(PROFILEKEY) + strlen(key_name)))) {
809 free(*section);
810 return NULL;
811 } // endif malloc
812
813 strcpy((*section)->key->name, key_name);
814 (*section)->key->value = NULL;
815 (*section)->key->next = NULL;
816 return (*section)->key;
817 } // end of PROFILE_Find
818
819
820 /***********************************************************************
821 * PROFILE_GetSection
822 *
823 * Returns all keys of a section.
824 * If return_values is TRUE, also include the corresponding values.
825 ***********************************************************************/
PROFILE_GetSection(PROFILESECTION * section,LPCSTR section_name,LPSTR buffer,uint len,BOOL handle_env,BOOL return_values)826 static int PROFILE_GetSection(PROFILESECTION *section, LPCSTR section_name,
827 LPSTR buffer, uint len,
828 BOOL handle_env, BOOL return_values)
829 {
830 PROFILEKEY *key;
831
832 if(!buffer)
833 return 0;
834
835 while (section) {
836 if (section->name[0] && !stricmp(section->name, section_name)) {
837 uint oldlen = len;
838
839 for (key = section->key; key; key = key->next) {
840 if (len <= 2)
841 break;
842
843 if (!*key->name)
844 continue; /* Skip empty lines */
845
846 if (IS_ENTRY_COMMENT(key->name))
847 continue; /* Skip comments */
848
849 PROFILE_CopyEntry(buffer, key->name, len - 1, handle_env);
850 len -= strlen(buffer) + 1;
851 buffer += strlen(buffer) + 1;
852
853 if (len < 2)
854 break;
855
856 if (return_values && key->value) {
857 buffer[-1] = '=';
858 PROFILE_CopyEntry(buffer, key->value, len - 1, handle_env);
859 len -= strlen(buffer) + 1;
860 buffer += strlen(buffer) + 1;
861 } // endif return_values
862
863 } // endfor key
864
865 *buffer = '\0';
866
867 if (len <= 1) {
868 /*If either lpszSection or lpszKey is NULL and the supplied
869 destination buffer is too small to hold all the strings,
870 the last string is truncated and followed by two null characters.
871 In this case, the return value is equal to cchReturnBuffer
872 minus two. */
873 buffer[-1] = '\0';
874 return oldlen - 2;
875 } // endif len
876
877 return oldlen - len;
878 } // endif section->name
879
880 section = section->next;
881 } // endwhile section
882
883 buffer[0] = buffer[1] = '\0';
884 return 0;
885 } // end of PROFILE_GetSection
886
887
888 /* See GetPrivateProfileSectionNamesA for documentation */
PROFILE_GetSectionNames(LPSTR buffer,uint len)889 static int PROFILE_GetSectionNames(LPSTR buffer, uint len)
890 {
891 LPSTR buf;
892 uint f,l;
893 PROFILESECTION *section;
894
895 if (trace(2))
896 htrc("GetSectionNames: buffer=%p len=%u\n", buffer, len);
897
898 if (!buffer || !len)
899 return 0;
900
901 if (len == 1) {
902 *buffer='\0';
903 return 0;
904 } // endif len
905
906 f = len - 1;
907 buf = buffer;
908 section = CurProfile->section;
909
910 if (trace(2))
911 htrc("GetSectionNames: section=%p\n", section);
912
913 while (section != NULL) {
914 if (trace(2))
915 htrc("section=%s\n", section->name);
916
917 if (section->name[0]) {
918 l = strlen(section->name) + 1;
919
920 if (trace(2))
921 htrc("l=%u f=%u\n", l, f);
922
923 if (l > f) {
924 if (f > 0) {
925 strncpy(buf, section->name, f-1);
926 buf += f-1;
927 *buf++='\0';
928 } // endif f
929
930 *buf = '\0';
931 return len - 2;
932 } // endif l
933
934 strcpy(buf, section->name);
935 buf += l;
936 f -= l;
937 } // endif section->name
938
939 section = section->next;
940 } // endwhile section
941
942 *buf='\0';
943 return buf-buffer;
944 } // end of PROFILE_GetSectionNames
945
946
947 /***********************************************************************
948 * PROFILE_GetString
949 *
950 * Get a profile string.
951 *
952 * Tests with GetPrivateProfileString16, W95a,
953 * with filled buffer ("****...") and section "set1" and key_name "1" valid:
954 * section key_name def_val res buffer
955 * "set1" "1" "x" 43 [data]
956 * "set1" "1 " "x" 43 [data] (!)
957 * "set1" " 1 "' "x" 43 [data] (!)
958 * "set1" "" "x" 1 "x"
959 * "set1" "" "x " 1 "x" (!)
960 * "set1" "" " x " 3 " x" (!)
961 * "set1" NULL "x" 6 "1\02\03\0\0"
962 * "set1" "" "x" 1 "x"
963 * NULL "1" "x" 0 "" (!)
964 * "" "1" "x" 1 "x"
965 * NULL NULL "" 0 ""
966 *
967 *************************************************************************/
PROFILE_GetString(LPCSTR section,LPCSTR key_name,LPCSTR def_val,LPSTR buffer,uint len)968 static int PROFILE_GetString(LPCSTR section, LPCSTR key_name,
969 LPCSTR def_val, LPSTR buffer, uint len)
970 {
971 PROFILEKEY *key = NULL;
972
973 if(!buffer)
974 return 0;
975
976 if (!def_val)
977 def_val = "";
978
979 if (key_name && key_name[0]) {
980 key = PROFILE_Find(&CurProfile->section, section, key_name, FALSE, FALSE);
981 PROFILE_CopyEntry(buffer, (key && key->value) ? key->value : def_val, len, FALSE);
982
983 if (trace(2))
984 htrc("('%s','%s','%s'): returning '%s'\n",
985 section, key_name, def_val, buffer );
986
987 return strlen(buffer);
988 } // endif key_name
989
990 if (key_name && !(key_name[0]))
991 /* Win95 returns 0 on keyname "". Tested with Likse32 bon 000227 */
992 return 0;
993
994 if (section && section[0])
995 return PROFILE_GetSection(CurProfile->section, section, buffer, len,
996 FALSE, FALSE);
997 buffer[0] = '\0';
998 return 0;
999 } // end of PROFILE_GetString
1000
1001
1002 /***********************************************************************
1003 * PROFILE_SetString
1004 *
1005 * Set a profile string.
1006 ***********************************************************************/
PROFILE_SetString(LPCSTR section_name,LPCSTR key_name,LPCSTR value,BOOL create_always)1007 static BOOL PROFILE_SetString(LPCSTR section_name, LPCSTR key_name,
1008 LPCSTR value, BOOL create_always)
1009 {
1010 if (!key_name) { /* Delete a whole section */
1011 if (trace(2))
1012 htrc("Deleting('%s')\n", section_name);
1013
1014 CurProfile->changed |= PROFILE_DeleteSection(&CurProfile->section,
1015 section_name);
1016 return TRUE; /* Even if PROFILE_DeleteSection() has failed,
1017 this is not an error on application's level.*/
1018 } else if (!value) { /* Delete a key */
1019 if (trace(2))
1020 htrc("Deleting('%s','%s')\n", section_name, key_name);
1021
1022 CurProfile->changed |= PROFILE_DeleteKey(&CurProfile->section,
1023 section_name, key_name);
1024 return TRUE; /* same error handling as above */
1025 } else { /* Set the key value */
1026 PROFILEKEY *key = PROFILE_Find(&CurProfile->section, section_name,
1027 key_name, TRUE, create_always);
1028 if (trace(2))
1029 htrc("Setting('%s','%s','%s')\n", section_name, key_name, value);
1030
1031 if (!key)
1032 return FALSE;
1033
1034 if (key->value) {
1035 /* strip the leading spaces. We can safely strip \n\r and
1036 * friends too, they should not happen here anyway. */
1037 while (PROFILE_isspace(*value))
1038 value++;
1039
1040 if (!strcmp(key->value, value)) {
1041 if (trace(2))
1042 htrc(" no change needed\n" );
1043
1044 return TRUE; /* No change needed */
1045 } // endif value
1046
1047 if (trace(2))
1048 htrc(" replacing '%s'\n", key->value);
1049
1050 free(key->value);
1051 } else if (trace(2))
1052 htrc(" creating key\n" );
1053
1054 key->value = (char*)malloc(strlen(value) + 1);
1055 strcpy(key->value, value);
1056 CurProfile->changed = TRUE;
1057 } // endelse
1058
1059 return TRUE;
1060 } // end of PROFILE_SetString
1061
1062
1063 /***********************************************************************
1064 * PROFILE_GetStringItem
1065 *
1066 * Convenience function that turns a string 'xxx, yyy, zzz' into
1067 * the 'xxx\0 yyy, zzz' and returns a pointer to the 'yyy, zzz'.
1068 ***********************************************************************/
1069 #if 0
1070 char *PROFILE_GetStringItem(char* start)
1071 {
1072 char *lpchX, *lpch;
1073
1074 for (lpchX = start, lpch = NULL; *lpchX != '\0'; lpchX++) {
1075 if (*lpchX == ',') {
1076 if (lpch)
1077 *lpch = '\0';
1078 else
1079 *lpchX = '\0';
1080
1081 while(*(++lpchX))
1082 if (!PROFILE_isspace(*lpchX))
1083 return lpchX;
1084
1085 } else if (PROFILE_isspace(*lpchX) && !lpch) {
1086 lpch = lpchX;
1087 } else
1088 lpch = NULL;
1089
1090 } // endfor lpchX
1091
1092 if (lpch)
1093 *lpch = '\0';
1094
1095 return NULL;
1096 } // end of PROFILE_GetStringItem
1097 #endif
1098
1099 /**********************************************************************
1100 * if allow_section_name_copy is TRUE, allow the copying :
1101 * - of Section names if 'section' is NULL
1102 * - of Keys in a Section if 'entry' is NULL
1103 * (see MSDN doc for GetPrivateProfileString)
1104 **********************************************************************/
PROFILE_GetPrivateProfileString(LPCSTR section,LPCSTR entry,LPCSTR def_val,LPSTR buffer,uint len,LPCSTR filename,BOOL allow_section_name_copy)1105 static int PROFILE_GetPrivateProfileString(LPCSTR section, LPCSTR entry,
1106 LPCSTR def_val, LPSTR buffer,
1107 uint len, LPCSTR filename,
1108 BOOL allow_section_name_copy)
1109 {
1110 int ret;
1111 LPSTR pDefVal = NULL;
1112
1113 if (!filename)
1114 filename = "win.ini";
1115
1116 /* strip any trailing ' ' of def_val. */
1117 if (def_val) {
1118 LPSTR p = (LPSTR)&def_val[strlen(def_val)]; // even "" works !
1119
1120 while (p > def_val)
1121 if ((*(--p)) != ' ')
1122 break;
1123
1124 if (*p == ' ') { /* ouch, contained trailing ' ' */
1125 int len = p - (LPSTR)def_val;
1126
1127 pDefVal = (LPSTR)malloc(len + 1);
1128 strncpy(pDefVal, def_val, len);
1129 pDefVal[len] = '\0';
1130 } // endif *p
1131
1132 } // endif def_val
1133
1134 if (!pDefVal)
1135 pDefVal = (LPSTR)def_val;
1136
1137 EnterCriticalSection(&PROFILE_CritSect);
1138
1139 if (PROFILE_Open(filename)) {
1140 if ((allow_section_name_copy) && (section == NULL))
1141 ret = PROFILE_GetSectionNames(buffer, len);
1142 else
1143 /* PROFILE_GetString already handles the 'entry == NULL' case */
1144 ret = PROFILE_GetString(section, entry, pDefVal, buffer, len);
1145
1146 } else {
1147 strncpy(buffer, pDefVal, len);
1148 ret = strlen(buffer);
1149 } // endif Open
1150
1151 LeaveCriticalSection(&PROFILE_CritSect);
1152
1153 if (pDefVal != def_val) /* allocated */
1154 memfree(pDefVal);
1155
1156 return ret;
1157 } // end of PROFILE_GetPrivateProfileString
1158
1159 /********************** API functions **********************************/
1160
1161 /***********************************************************************
1162 * GetPrivateProfileStringA (KERNEL32.@)
1163 ***********************************************************************/
GetPrivateProfileString(LPCSTR section,LPCSTR entry,LPCSTR def_val,LPSTR buffer,DWORD len,LPCSTR filename)1164 int GetPrivateProfileString(LPCSTR section, LPCSTR entry, LPCSTR def_val,
1165 LPSTR buffer, DWORD len, LPCSTR filename)
1166 {
1167 return PROFILE_GetPrivateProfileString(section, entry, def_val,
1168 buffer, len, filename, TRUE);
1169 } // end of GetPrivateProfileString
1170
1171
1172 /***********************************************************************
1173 * GetPrivateProfileIntA (KERNEL32.@)
1174 ***********************************************************************/
GetPrivateProfileInt(LPCSTR section,LPCSTR entry,int def_val,LPCSTR filename)1175 uint GetPrivateProfileInt(LPCSTR section, LPCSTR entry,
1176 int def_val, LPCSTR filename)
1177 {
1178 char buffer[20];
1179 int result;
1180
1181 if (!PROFILE_GetPrivateProfileString(section, entry, "", buffer,
1182 sizeof(buffer), filename, FALSE))
1183 return def_val;
1184
1185 /* FIXME: if entry can be found but it's empty, then Win16 is
1186 * supposed to return 0 instead of def_val ! Difficult/problematic
1187 * to implement (every other failure also returns zero buffer),
1188 * thus wait until testing framework avail for making sure nothing
1189 * else gets broken that way. */
1190 if (!buffer[0])
1191 return (uint)def_val;
1192
1193 /* Don't use strtol() here !
1194 * (returns LONG_MAX/MIN on overflow instead of "proper" overflow)
1195 YES, scan for unsigned format ! (otherwise compatibility error) */
1196 if (!sscanf(buffer, "%u", &result))
1197 return 0;
1198
1199 return (uint)result;
1200 } // end of GetPrivateProfileInt
1201
1202
1203 /***********************************************************************
1204 * GetPrivateProfileSectionA (KERNEL32.@)
1205 ***********************************************************************/
GetPrivateProfileSection(LPCSTR section,LPSTR buffer,DWORD len,LPCSTR filename)1206 int GetPrivateProfileSection(LPCSTR section, LPSTR buffer,
1207 DWORD len, LPCSTR filename)
1208 {
1209 int ret = 0;
1210
1211 EnterCriticalSection( &PROFILE_CritSect );
1212
1213 if (PROFILE_Open(filename))
1214 ret = PROFILE_GetSection(CurProfile->section, section, buffer, len,
1215 FALSE, TRUE);
1216
1217 LeaveCriticalSection( &PROFILE_CritSect );
1218 return ret;
1219 } // end of GetPrivateProfileSection
1220
1221
1222 /***********************************************************************
1223 * WritePrivateProfileStringA (KERNEL32.@)
1224 ***********************************************************************/
WritePrivateProfileString(LPCSTR section,LPCSTR entry,LPCSTR string,LPCSTR filename)1225 BOOL WritePrivateProfileString(LPCSTR section, LPCSTR entry,
1226 LPCSTR string, LPCSTR filename)
1227 {
1228 BOOL ret = FALSE;
1229
1230 EnterCriticalSection( &PROFILE_CritSect );
1231
1232 if (PROFILE_Open(filename)) {
1233 if (!section && !entry && !string) /* documented "file flush" case */
1234 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1235 else {
1236 if (!section) {
1237 //FIXME("(NULL?,%s,%s,%s)? \n",entry,string,filename);
1238 } else {
1239 ret = PROFILE_SetString(section, entry, string, FALSE);
1240
1241 if (ret)
1242 ret = PROFILE_FlushFile();
1243
1244 } // endif section
1245
1246 } // endif section || entry|| string
1247
1248 } // endif Open
1249
1250 LeaveCriticalSection( &PROFILE_CritSect );
1251 return ret;
1252 } // end of WritePrivateProfileString
1253
1254
1255 /***********************************************************************
1256 * WritePrivateProfileSectionA (KERNEL32.@)
1257 ***********************************************************************/
WritePrivateProfileSection(LPCSTR section,LPCSTR string,LPCSTR filename)1258 BOOL WritePrivateProfileSection(LPCSTR section,
1259 LPCSTR string, LPCSTR filename )
1260 {
1261 BOOL ret = FALSE;
1262 LPSTR p ;
1263
1264 EnterCriticalSection(&PROFILE_CritSect);
1265
1266 if (PROFILE_Open(filename)) {
1267 if (!section && !string)
1268 PROFILE_ReleaseFile(); /* always return FALSE in this case */
1269 else if (!string) { /* delete the named section*/
1270 ret = PROFILE_SetString(section, NULL, NULL, FALSE);
1271
1272 if (ret)
1273 ret = PROFILE_FlushFile();
1274 } else {
1275 PROFILE_DeleteAllKeys(section);
1276 ret = TRUE;
1277
1278 while (*string) {
1279 LPSTR buf = (LPSTR)malloc(strlen(string) + 1);
1280 strcpy(buf, string);
1281
1282 if ((p = strchr(buf, '='))) {
1283 *p='\0';
1284 ret = PROFILE_SetString(section, buf, p+1, TRUE);
1285 } // endif p
1286
1287 free(buf);
1288 string += strlen(string) + 1;
1289
1290 if (ret)
1291 ret = PROFILE_FlushFile();
1292
1293 } // endwhile *string
1294
1295 } // endelse
1296
1297 } // endif Open
1298
1299 LeaveCriticalSection(&PROFILE_CritSect);
1300 return ret;
1301 } // end of WritePrivateProfileSection
1302
1303
1304 /***********************************************************************
1305 * GetPrivateProfileSectionNamesA (KERNEL32.@)
1306 *
1307 * Returns the section names contained in the specified file.
1308 * FIXME: Where do we find this file when the path is relative?
1309 * The section names are returned as a list of strings with an extra
1310 * '\0' to mark the end of the list. Except for that the behavior
1311 * depends on the Windows version.
1312 *
1313 * Win95:
1314 * - if the buffer is 0 or 1 character long then it is as if it was of
1315 * infinite length.
1316 * - otherwise, if the buffer is to small only the section names that fit
1317 * are returned.
1318 * - note that this means if the buffer was to small to return even just
1319 * the first section name then a single '\0' will be returned.
1320 * - the return value is the number of characters written in the buffer,
1321 * except if the buffer was too smal in which case len-2 is returned
1322 *
1323 * Win2000:
1324 * - if the buffer is 0, 1 or 2 characters long then it is filled with
1325 * '\0' and the return value is 0
1326 * - otherwise if the buffer is too small then the first section name that
1327 * does not fit is truncated so that the string list can be terminated
1328 * correctly (double '\0')
1329 * - the return value is the number of characters written in the buffer
1330 * except for the trailing '\0'. If the buffer is too small, then the
1331 * return value is len-2
1332 * - Win2000 has a bug that triggers when the section names and the
1333 * trailing '\0' fit exactly in the buffer. In that case the trailing
1334 * '\0' is missing.
1335 *
1336 * Wine implements the observed Win2000 behavior (except for the bug).
1337 *
1338 * Note that when the buffer is big enough then the return value may be any
1339 * value between 1 and len-1 (or len in Win95), including len-2.
1340 */
1341 #ifdef TEST_MODULE
1342 static DWORD
GetPrivateProfileSectionNames(LPSTR buffer,DWORD size,LPCSTR filename)1343 GetPrivateProfileSectionNames(LPSTR buffer, DWORD size, LPCSTR filename)
1344 {
1345 DWORD ret = 0;
1346
1347 if (trace(2))
1348 htrc("GPPSN: filename=%s\n", filename);
1349
1350 EnterCriticalSection(&PROFILE_CritSect);
1351
1352 if (PROFILE_Open(filename))
1353 ret = PROFILE_GetSectionNames(buffer, size);
1354
1355 LeaveCriticalSection(&PROFILE_CritSect);
1356 return ret;
1357 } // end of GetPrivateProfileSectionNames
1358
1359
1360 /************************************************************************
1361 * Program to test the above
1362 ************************************************************************/
main(int argc,char ** argv)1363 int main(int argc, char**argv) {
1364 char buff[128];
1365 char *p, *inifile = "D:\\Plug\\Data\\contact.ini";
1366 DWORD n;
1367
1368 n = GetPrivateProfileSectionNames(buff, 128, inifile);
1369 printf("Sections: n=%d\n", n);
1370
1371 for (p = buff; *p; p += (strlen(p) + 1))
1372 printf("section=[%s]\n", p);
1373
1374 GetPrivateProfileString("BER", "name", "?", buff, 128, inifile);
1375 printf("[BER](name) = %s\n", buff);
1376
1377 WritePrivateProfileString("FOO", "city", NULL, inifile);
1378 GetPrivateProfileString("FOO", "city", "?", buff, 128, inifile);
1379 printf("[FOO](city) = %s\n", buff);
1380
1381 printf("FOO city: ");
1382 fgets(buff, sizeof(buff), stdin);
1383 if (buff[strlen(buff) - 1] == '\n')
1384 buff[strlen(buff) - 1] = '\0';
1385 WritePrivateProfileString("FOO", "city", buff, inifile);
1386 GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile);
1387 printf("After write, [FOO](City) = %s\n", buff);
1388
1389 printf("New city: ");
1390 fgets(buff, sizeof(buff), stdin);
1391 if (buff[strlen(buff) - 1] == '\n')
1392 buff[strlen(buff) - 1] = '\0';
1393 WritePrivateProfileString("FOO", "city", buff, inifile);
1394 GetPrivateProfileString("FOO", "city", "???", buff, 128, inifile);
1395 printf("After update, [FOO](City) = %s\n", buff);
1396
1397 printf("FOO name: ");
1398 fgets(buff, sizeof(buff), stdin);
1399 if (buff[strlen(buff) - 1] == '\n')
1400 buff[strlen(buff) - 1] = '\0';
1401 WritePrivateProfileString("FOO", "name", buff, inifile);
1402 GetPrivateProfileString("FOO", "name", "X", buff, 128, inifile);
1403 printf("[FOO](name) = %s\n", buff);
1404 } // end of main
1405 #endif // TEST_MODULE
1406