Release 950727
[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     while ((c = fgetc (f)) != EOF){
138         if (c == '\r')          /* Ignore Carriage Return */
139             continue;
140         
141         switch (state){
142
143         case OnSecHeader:
144             if (c == ']' || overflow){
145                 *next = '\0';
146                 next = CharBuffer;
147                 SecHeader->AppName = strdup (CharBuffer);
148                 state = IgnoreToEOL;
149                 dprintf_profile(stddeb,"%s: section %s\n", file, CharBuffer);
150             } else
151                 *next++ = c;
152             break;
153
154         case IgnoreToEOL:
155             if (c == '\n'){
156                 state = KeyDef;
157                 next = CharBuffer;
158             }
159             break;
160
161         case FirstBrace:
162         case KeyDef:
163             if (c == '['){
164                 TSecHeader *temp;
165                 
166                 temp = SecHeader;
167                 SecHeader = (TSecHeader *) xmalloc (sizeof (TSecHeader));
168                 SecHeader->link = temp;
169                 SecHeader->Keys = 0;
170                 state = OnSecHeader;
171                 next = CharBuffer;
172                 break;
173             }
174             if (state == FirstBrace) /* On first pass, don't allow dangling keys */
175                 break;
176
177             if (c == '\t')
178                 break;
179             
180             if (c == '\n' || c == ';' || overflow) /* Abort Definition */
181                 next = CharBuffer;
182
183             if (c == ';')
184             {
185                 state = IgnoreToEOL;
186                 break;
187             }
188
189             if (c == '\n')
190               break;
191             
192             if (c == '=' || overflow){
193                 TKeys *temp;
194
195                 temp = SecHeader->Keys;
196                 while(next[-1]==' ')next--;
197                 *next = '\0';
198                 SecHeader->Keys = (TKeys *) xmalloc (sizeof (TKeys));
199                 SecHeader->Keys->link = temp;
200                 SecHeader->Keys->KeyName = strdup (CharBuffer);
201                 state = KeyValue;
202                 next = CharBuffer;
203                 dprintf_profile(stddeb,"%s:   key %s\n", file, CharBuffer);
204             } else {
205                 *next++ = c;
206             }
207             break;
208
209         case KeyValue:
210             if (overflow || c == '\n'){
211                 *next = '\0';
212                 SecHeader->Keys->Value = strdup (CharBuffer);
213                 state = c == '\n' ? KeyDef : IgnoreToEOL;
214                 next = CharBuffer;
215                 dprintf_profile (stddeb, "[%s] (%s)=%s\n", SecHeader->AppName,
216                         SecHeader->Keys->KeyName, SecHeader->Keys->Value);
217             } else
218                 *next++ = c;
219             break;
220             
221         } /* switch */
222         
223     } /* while ((c = fgetc (f)) != EOF) */
224     return SecHeader;
225 }
226
227 static void new_key (TSecHeader *section, char *KeyName, char *Value)
228 {
229     TKeys *key;
230     
231     key = (TKeys *) xmalloc (sizeof (TKeys));
232     key->KeyName = strdup (KeyName);
233     key->Value   = strdup (Value);
234     key->link = section->Keys;
235     section->Keys = key;
236 }
237
238 static short GetSetProfile (int set, LPSTR AppName, LPSTR KeyName,
239                      LPSTR Default, LPSTR ReturnedString, short Size,
240                      LPSTR FileName)
241
242 {
243     TProfile   *New;
244     TSecHeader *section;
245     TKeys      *key;
246     
247     /* Supposedly Default should NEVER be NULL.  But sometimes it is.  */
248     if (Default == NULL)
249         Default = "";
250
251     if (!(section = is_loaded (FileName))){
252         New = (TProfile *) xmalloc (sizeof (TProfile));
253         New->link = Base;
254         New->FileName = strdup (FileName);
255         New->Section = load (FileName, &New->FullName);
256         New->changed = FALSE;
257         Base = New;
258         section = New->Section;
259         Current = New;
260     }
261     /* Start search */
262     for (; section; section = section->link){
263         if (strcasecmp (section->AppName, AppName))
264             continue;
265
266         /* If no key value given, then list all the keys */
267         if ((!KeyName) && (!set)){
268             char *p = ReturnedString;
269             int left = Size - 2;
270             int slen;
271
272             dprintf_profile(stddeb,"GetSetProfile // KeyName == NULL, Enumeration !\n");
273             for (key = section->Keys; key; key = key->link){
274                 if (left < 1) {
275                         dprintf_profile(stddeb,"GetSetProfile // No more storage for enum !\n");
276                         return (Size - 2);
277                 }
278                 slen = min(strlen(key->KeyName) + 1, left);
279                 dprintf_profile(stddeb,"GetSetProfile // strncpy(%p, %p, %d);\n", 
280                                 ReturnedString, key->Value, slen);
281                 strncpy (p, key->KeyName, slen);
282                 dprintf_profile(stddeb,"GetSetProfile // enum '%s' !\n", p);
283                 left -= slen;
284                 p += slen;
285             }
286                 *p = '\0';
287                 dprintf_profile(stddeb,"GetSetProfile // normal end of enum !\n");
288             return (Size - 2 - left);
289         }
290         for (key = section->Keys; key; key = key->link){
291             int slen;
292             if (strcasecmp (key->KeyName, KeyName))
293                 continue;
294             if (set){
295                 free (key->Value);
296                 key->Value = strdup (Default ? Default : "");
297                 Current->changed=TRUE;
298                 return 1;
299             }
300             slen = min(strlen(key->Value), Size - 1);
301             ReturnedString[slen] = 0;
302             strncpy (ReturnedString, key->Value, slen);
303             dprintf_profile(stddeb,"GetSetProfile // Return ``%s''\n", ReturnedString);
304             return 1; 
305         }
306         /* If Getting the information, then don't write the information
307            to the INI file, need to run a couple of tests with windog */
308         /* No key found */
309         if (set) {
310             new_key (section, KeyName, Default);
311         } else {
312             int slen = min(strlen(Default), Size - 1);
313             ReturnedString[slen] = 0;
314             strncpy(ReturnedString, Default, slen);
315             dprintf_profile(stddeb,"GetSetProfile // Key not found\n");
316         }
317         return 1;
318     }
319     /* Non existent section */
320     if (set){
321         section = (TSecHeader *) xmalloc (sizeof (TSecHeader));
322         section->AppName = strdup (AppName);
323         section->Keys = 0;
324         new_key (section, KeyName, Default);
325         section->link = Current->Section;
326         Current->Section = section;
327         Current->changed = TRUE;
328     } else {
329         int slen = min(strlen(Default), Size - 1);
330         ReturnedString[slen] = 0;
331         strncpy(ReturnedString, Default, slen);
332         dprintf_profile(stddeb,"GetSetProfile // Section not found\n");
333     }
334     return 1;
335 }
336
337 short GetPrivateProfileString (LPSTR AppName, LPSTR KeyName,
338                                LPSTR Default, LPSTR ReturnedString,
339                                short Size, LPSTR FileName)
340 {
341     int v;
342
343     dprintf_profile(stddeb,"GetPrivateProfileString ('%s', '%s', '%s', %p, %d, %s\n", 
344                         AppName, KeyName, Default, ReturnedString, Size, FileName);
345     v = GetSetProfile (0,AppName,KeyName,Default,ReturnedString,Size,FileName);
346     if (AppName)
347         return strlen (ReturnedString);
348     else
349         return Size - v;
350 }
351
352 int GetProfileString (LPSTR AppName, LPSTR KeyName, LPSTR Default, 
353                       LPSTR ReturnedString, int Size)
354 {
355     return GetPrivateProfileString (AppName, KeyName, Default,
356                                     ReturnedString, Size, WIN_INI);
357 }
358
359 WORD GetPrivateProfileInt (LPSTR AppName, LPSTR KeyName, short Default,
360                            LPSTR File)
361 {
362     static char IntBuf[10];
363     static char buf[10];
364
365     sprintf (buf, "%d", Default);
366     
367     /* Check the exact semantic with the SDK */
368     GetPrivateProfileString (AppName, KeyName, buf, IntBuf, 10, File);
369     if (!strcasecmp (IntBuf, "true"))
370         return 1;
371     if (!strcasecmp (IntBuf, "yes"))
372         return 1;
373     return strtoul( IntBuf, NULL, 0 );
374 }
375
376 WORD GetProfileInt (LPSTR AppName, LPSTR KeyName, int Default)
377 {
378     return GetPrivateProfileInt (AppName, KeyName, Default, WIN_INI);
379 }
380
381 BOOL WritePrivateProfileString (LPSTR AppName, LPSTR KeyName, LPSTR String,
382                                 LPSTR FileName)
383 {
384     return GetSetProfile (1, AppName, KeyName, String, "", 0, FileName);
385 }
386
387 BOOL WriteProfileString (LPSTR AppName, LPSTR KeyName, LPSTR String)
388 {
389     return (WritePrivateProfileString (AppName, KeyName, String, WIN_INI));
390 }
391
392 static void dump_keys (FILE *profile, TKeys *p)
393 {
394     if (!p)
395         return;
396     dump_keys (profile, p->link);
397     fprintf (profile, "%s=%s\r\n", p->KeyName, p->Value);
398 }
399
400 static void dump_sections (FILE *profile, TSecHeader *p)
401 {
402     if (!p)
403         return;
404     dump_sections (profile, p->link);
405     fprintf (profile, "\r\n[%s]\r\n", p->AppName);
406     dump_keys (profile, p->Keys);
407 }
408
409 static void dump_profile (TProfile *p)
410 {
411     FILE *profile;
412     
413     if (!p)
414         return;
415     dump_profile (p->link);
416     if(!p->changed)
417         return;
418     if ((profile = fopen (p->FullName, "w")) != NULL){
419         dump_sections (profile, p->Section);
420         fclose (profile);
421     }
422 }
423
424 void sync_profiles (void)
425 {
426     dump_profile (Base);
427 }