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  = &section->next;
291         next_key      = &section->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