Release 950901
[wine] / misc / profile.c
1 /*
2  * Initialization-File Functions.
3  *
4  * Copyright (c) 1993 Miguel de Icaza
5  *
6  * 1/Dec o Corrected return values for Get*ProfileString
7  *
8  *       o Now, if AppName == NULL in Get*ProfileString it returns a list
9  *            of the KeyNames (as documented in the MS-SDK).
10  *
11  *       o if KeyValue == NULL now clears the value in Get*ProfileString
12  *
13  * 20/Apr  SL - I'm not sure where these definitions came from, but my SDK
14  *         has a NULL KeyValue returning a list of KeyNames, and a NULL
15  *         AppName undefined.  I changed GetSetProfile to match.  This makes
16  *         PROGMAN.EXE do the right thing.
17  *
18 static char Copyright [] = "Copyright (C) 1993 Miguel de Icaza";
19 */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "wine.h"
25 #include "windows.h"
26 #include "dos_fs.h"
27 #include "module.h"
28 #include "toolhelp.h"
29 #include "stddebug.h"
30 /* #define DEBUG_PROFILE */
31 #include "debug.h"
32
33 #define STRSIZE 255
34 #define xmalloc(x) malloc(x)
35 #define overflow (next == &CharBuffer [STRSIZE-1])
36
37 enum { FirstBrace, OnSecHeader, IgnoreToEOL, KeyDef, KeyValue };
38
39 typedef struct TKeys {
40     char *KeyName;
41     char *Value;
42     struct TKeys *link;
43 } TKeys;
44
45 typedef struct TSecHeader {
46     char *AppName;
47     TKeys *Keys;
48     struct TSecHeader *link;
49 } TSecHeader;
50     
51 typedef struct TProfile {
52     char *FileName;
53     char *FullName;
54     TSecHeader *Section;
55     struct TProfile *link;
56     int changed;
57 } TProfile;
58
59 TProfile *Current = 0;
60 TProfile *Base = 0;
61
62 static TSecHeader *is_loaded (char *FileName)
63 {
64     TProfile *p = Base;
65     
66     while (p){
67         if (!strcasecmp (FileName, p->FileName)){
68             Current = p;
69             return p->Section;
70         }
71         p = p->link;
72     }
73     return 0;
74 }
75
76 static char *GetIniFileName(char *name, char *dir)
77 {
78         char temp[256];
79
80         if (strchr(name, '/'))
81                 return name;
82
83         if (strchr(name, '\\'))
84                 return DOS_GetUnixFileName(name);
85
86         strcpy(temp, dir);
87         strcat(temp, "\\");
88         strcat(temp, name);
89         
90         return DOS_GetUnixFileName(temp);
91 }
92
93 static TSecHeader *load (char *filename, char **pfullname)
94 {
95     FILE *f;
96     int state;
97     TSecHeader *SecHeader = 0;
98     char CharBuffer [STRSIZE];
99     char *next = '\0';
100     char *file;
101     char c;
102     char path[MAX_PATH+1];
103
104     /* Try the Windows directory */
105
106     GetWindowsDirectory(path, sizeof(path));
107     file = GetIniFileName(filename, path);
108
109     dprintf_profile(stddeb,"Load %s\n", file);
110     f = fopen(file, "r");
111     
112     if (f == NULL) {
113         /* Try the path of the current executable */
114     
115         if (GetCurrentTask())
116         {
117             char *p;
118             GetModuleFileName( GetCurrentTask(), path, MAX_PATH );
119             if ((p = strrchr( path, '\\' )))
120             {
121                 p[1] = '\0';
122                 file = GetIniFileName(filename, path);
123                 f = fopen(file, "r");
124             }
125         }
126     }
127     if (f == NULL) {
128         fprintf(stderr, "profile.c: load() can't find file %s\n", filename);
129         return NULL;
130     }
131     
132     *pfullname = strdup(file);
133     dprintf_profile(stddeb,"Loading %s\n", file);
134
135
136     state = FirstBrace;
137     next = CharBuffer;
138     while ((c = fgetc (f)) != EOF){
139         if (c == '\r')          /* Ignore Carriage Return */
140             continue;
141         
142         switch (state){
143
144         case OnSecHeader:
145             if (c == ']' || overflow){
146                 *next = '\0';
147                 next = CharBuffer;
148                 SecHeader->AppName = strdup (CharBuffer);
149                 state = IgnoreToEOL;
150                 dprintf_profile(stddeb,"%s: section %s\n", file, CharBuffer);
151             } else
152                 *next++ = c;
153             break;
154
155         case IgnoreToEOL:
156             if (c == '\n'){
157                 state = KeyDef;
158                 next = CharBuffer;
159             }
160             break;
161
162         case FirstBrace:
163         case KeyDef:
164             if (c == '['){
165                 TSecHeader *temp;
166                 
167                 temp = SecHeader;
168                 SecHeader = (TSecHeader *) xmalloc (sizeof (TSecHeader));
169                 SecHeader->link = temp;
170                 SecHeader->Keys = 0;
171                 state = OnSecHeader;
172                 next = CharBuffer;
173                 break;
174             }
175             if (state == FirstBrace) /* On first pass, don't allow dangling keys */
176                 break;
177
178             if (c == '\t')
179                 break;
180             
181             if (c == '\n' || c == ';' || overflow) /* Abort Definition */
182                 next = CharBuffer;
183
184             if (c == ';')
185             {
186                 state = IgnoreToEOL;
187                 break;
188             }
189
190             if (c == '\n')
191               break;
192             
193             if (c == '=' || overflow){
194                 TKeys *temp;
195
196                 temp = SecHeader->Keys;
197                 while(next[-1]==' ')next--;
198                 *next = '\0';
199                 SecHeader->Keys = (TKeys *) xmalloc (sizeof (TKeys));
200                 SecHeader->Keys->link = temp;
201                 SecHeader->Keys->KeyName = strdup (CharBuffer);
202                 state = KeyValue;
203                 next = CharBuffer;
204                 dprintf_profile(stddeb,"%s:   key %s\n", file, CharBuffer);
205             } else {
206                 *next++ = c;
207             }
208             break;
209
210         case KeyValue:
211             if (overflow || c == '\n'){
212                 *next = '\0';
213                 SecHeader->Keys->Value = strdup (CharBuffer);
214                 state = c == '\n' ? KeyDef : IgnoreToEOL;
215                 next = CharBuffer;
216                 dprintf_profile (stddeb, "[%s] (%s)=%s\n", SecHeader->AppName,
217                         SecHeader->Keys->KeyName, SecHeader->Keys->Value);
218             } else
219                 *next++ = c;
220             break;
221             
222         } /* switch */
223         
224     } /* while ((c = fgetc (f)) != EOF) */
225     return SecHeader;
226 }
227
228 static void new_key (TSecHeader *section, char *KeyName, char *Value)
229 {
230     TKeys *key;
231     
232     key = (TKeys *) xmalloc (sizeof (TKeys));
233     key->KeyName = strdup (KeyName);
234     key->Value   = strdup (Value);
235     key->link = section->Keys;
236     section->Keys = key;
237 }
238
239 static short GetSetProfile (int set, LPSTR AppName, LPSTR KeyName,
240                      LPSTR Default, LPSTR ReturnedString, short Size,
241                      LPSTR FileName)
242
243 {
244     TProfile   *New;
245     TSecHeader *section;
246     TKeys      *key;
247     
248     /* Supposedly Default should NEVER be NULL.  But sometimes it is.  */
249     if (Default == NULL)
250         Default = "";
251
252     if (!(section = is_loaded (FileName))){
253         New = (TProfile *) xmalloc (sizeof (TProfile));
254         New->link = Base;
255         New->FileName = strdup (FileName);
256         New->Section = load (FileName, &New->FullName);
257         New->changed = FALSE;
258         Base = New;
259         section = New->Section;
260         Current = New;
261     }
262     /* Start search */
263     for (; section; section = section->link){
264         if (strcasecmp (section->AppName, AppName))
265             continue;
266
267         /* If no key value given, then list all the keys */
268         if ((!KeyName) && (!set)){
269             char *p = ReturnedString;
270             int left = Size - 2;
271             int slen;
272
273             dprintf_profile(stddeb,"GetSetProfile // KeyName == NULL, Enumeration !\n");
274             for (key = section->Keys; key; key = key->link){
275                 if (left < 1) {
276                         dprintf_profile(stddeb,"GetSetProfile // No more storage for enum !\n");
277                         return (Size - 2);
278                 }
279                 slen = min(strlen(key->KeyName) + 1, left);
280                 dprintf_profile(stddeb,"GetSetProfile // strncpy(%p, %p, %d);\n", 
281                                 ReturnedString, key->Value, slen);
282                 strncpy (p, key->KeyName, slen);
283                 dprintf_profile(stddeb,"GetSetProfile // enum '%s' !\n", p);
284                 left -= slen;
285                 p += slen;
286             }
287                 *p = '\0';
288                 dprintf_profile(stddeb,"GetSetProfile // normal end of enum !\n");
289             return (Size - 2 - left);
290         }
291         for (key = section->Keys; key; key = key->link){
292             int slen;
293             if (strcasecmp (key->KeyName, KeyName))
294                 continue;
295             if (set){
296                 free (key->Value);
297                 key->Value = strdup (Default ? Default : "");
298                 Current->changed=TRUE;
299                 return 1;
300             }
301             slen = min(strlen(key->Value), Size - 1);
302             ReturnedString[slen] = 0;
303             strncpy (ReturnedString, key->Value, slen);
304             dprintf_profile(stddeb,"GetSetProfile // Return ``%s''\n", ReturnedString);
305             return 1; 
306         }
307         /* If Getting the information, then don't write the information
308            to the INI file, need to run a couple of tests with windog */
309         /* No key found */
310         if (set) {
311             new_key (section, KeyName, Default);
312         } else {
313             int slen = min(strlen(Default), Size - 1);
314             ReturnedString[slen] = 0;
315             strncpy(ReturnedString, Default, slen);
316             dprintf_profile(stddeb,"GetSetProfile // Key not found\n");
317         }
318         return 1;
319     }
320     /* Non existent section */
321     if (set){
322         section = (TSecHeader *) xmalloc (sizeof (TSecHeader));
323         section->AppName = strdup (AppName);
324         section->Keys = 0;
325         new_key (section, KeyName, Default);
326         section->link = Current->Section;
327         Current->Section = section;
328         Current->changed = TRUE;
329     } else {
330         int slen = min(strlen(Default), Size - 1);
331         ReturnedString[slen] = 0;
332         strncpy(ReturnedString, Default, slen);
333         dprintf_profile(stddeb,"GetSetProfile // Section not found\n");
334     }
335     return 1;
336 }
337
338 short GetPrivateProfileString (LPSTR AppName, LPSTR KeyName,
339                                LPSTR Default, LPSTR ReturnedString,
340                                short Size, LPSTR FileName)
341 {
342     int v;
343
344     dprintf_profile(stddeb,"GetPrivateProfileString ('%s', '%s', '%s', %p, %d, %s\n", 
345                         AppName, KeyName, Default, ReturnedString, Size, FileName);
346     v = GetSetProfile (0,AppName,KeyName,Default,ReturnedString,Size,FileName);
347     if (AppName)
348         return strlen (ReturnedString);
349     else
350         return Size - v;
351 }
352
353 int GetProfileString (LPSTR AppName, LPSTR KeyName, LPSTR Default, 
354                       LPSTR ReturnedString, int Size)
355 {
356     return GetPrivateProfileString (AppName, KeyName, Default,
357                                     ReturnedString, Size, WIN_INI);
358 }
359
360 WORD GetPrivateProfileInt (LPSTR AppName, LPSTR KeyName, short Default,
361                            LPSTR File)
362 {
363     static char IntBuf[10];
364     static char buf[10];
365
366     sprintf (buf, "%d", Default);
367     
368     /* Check the exact semantic with the SDK */
369     GetPrivateProfileString (AppName, KeyName, buf, IntBuf, 10, File);
370     if (!strcasecmp (IntBuf, "true"))
371         return 1;
372     if (!strcasecmp (IntBuf, "yes"))
373         return 1;
374     return strtoul( IntBuf, NULL, 0 );
375 }
376
377 WORD GetProfileInt (LPSTR AppName, LPSTR KeyName, int Default)
378 {
379     return GetPrivateProfileInt (AppName, KeyName, Default, WIN_INI);
380 }
381
382 BOOL WritePrivateProfileString (LPSTR AppName, LPSTR KeyName, LPSTR String,
383                                 LPSTR FileName)
384 {
385     if (!AppName || !KeyName || !String)  /* Flush file to disk */
386         return TRUE;
387     return GetSetProfile (1, AppName, KeyName, String, "", 0, FileName);
388 }
389
390 BOOL WriteProfileString (LPSTR AppName, LPSTR KeyName, LPSTR String)
391 {
392     return (WritePrivateProfileString (AppName, KeyName, String, WIN_INI));
393 }
394
395 static void dump_keys (FILE *profile, TKeys *p)
396 {
397     if (!p)
398         return;
399     dump_keys (profile, p->link);
400     fprintf (profile, "%s=%s\r\n", p->KeyName, p->Value);
401 }
402
403 static void dump_sections (FILE *profile, TSecHeader *p)
404 {
405     if (!p)
406         return;
407     dump_sections (profile, p->link);
408     fprintf (profile, "\r\n[%s]\r\n", p->AppName);
409     dump_keys (profile, p->Keys);
410 }
411
412 static void dump_profile (TProfile *p)
413 {
414     FILE *profile;
415     
416     if (!p)
417         return;
418     dump_profile (p->link);
419     if(!p->changed)
420         return;
421     if ((profile = fopen (p->FullName, "w")) != NULL){
422         dump_sections (profile, p->Section);
423         fclose (profile);
424     }
425 }
426
427 void sync_profiles (void)
428 {
429     dump_profile (Base);
430 }