Fix gcc 4.0 warnings.
[wine] / programs / winecfg / winecfg.c
1 /*
2  * WineCfg configuration management
3  *
4  * Copyright 2002 Jaco Greeff
5  * Copyright 2003 Dimitrie O. Paun
6  * Copyright 2003-2004 Mike Hearn
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * TODO:
23  *  - Use unicode
24  *  - Icons in listviews/icons
25  *  - Better add app dialog, scan c: for EXE files and add to list in background
26  *  - Use [GNOME] HIG style groupboxes rather than win32 style (looks nicer, imho)
27  *
28  */
29
30 #include <assert.h>
31 #include <stdio.h>
32 #include <limits.h>
33 #include <windows.h>
34 #include <winreg.h>
35 #include <wine/debug.h>
36 #include <wine/list.h>
37
38 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
39
40 #include "winecfg.h"
41
42 HKEY config_key = NULL;
43
44
45
46 /* this is called from the WM_SHOWWINDOW handlers of each tab page.
47  *
48  * it's a nasty hack, necessary because the property sheet insists on resetting the window title
49  * to the title of the tab, which is utterly useless. dropping the property sheet is on the todo list.
50  */
51 void set_window_title(HWND dialog)
52 {
53     char *newtitle;
54
55     /* update the window title  */
56     if (current_app)
57     {
58         const char *template = "Wine Configuration for %s";
59         newtitle = HeapAlloc(GetProcessHeap(), 0, strlen(template) + strlen(current_app) + 1);
60         sprintf(newtitle, template, current_app);
61     }
62     else
63     {
64         newtitle = strdupA("Wine Configuration");
65     }
66
67     WINE_TRACE("setting title to %s\n", newtitle);
68     SendMessage(GetParent(dialog), PSM_SETTITLE, 0, (LPARAM) newtitle);
69     HeapFree(GetProcessHeap(), 0, newtitle);
70 }
71
72
73 /**
74  * get_config_key: Retrieves a configuration value from the registry
75  *
76  * char *subkey : the name of the config section
77  * char *name : the name of the config value
78  * char *default : if the key isn't found, return this value instead
79  *
80  * Returns a buffer holding the value if successful, NULL if
81  * not. Caller is responsible for releasing the result.
82  *
83  */
84 static char *get_config_key (HKEY root, const char *subkey, const char *name, const char *def)
85 {
86     LPSTR buffer = NULL;
87     DWORD len;
88     HKEY hSubKey = NULL;
89     DWORD res;
90
91     WINE_TRACE("subkey=%s, name=%s, def=%s\n", subkey, name, def);
92
93     res = RegOpenKey(root, subkey, &hSubKey);
94     if (res != ERROR_SUCCESS)
95     {
96         if (res == ERROR_FILE_NOT_FOUND)
97         {
98             WINE_TRACE("Section key not present - using default\n");
99             return def ? strdupA(def) : NULL;
100         }
101         else
102         {
103             WINE_ERR("RegOpenKey failed on wine config key (res=%ld)\n", res);
104         }
105         goto end;
106     }
107
108     res = RegQueryValueExA(hSubKey, name, NULL, NULL, NULL, &len);
109     if (res == ERROR_FILE_NOT_FOUND)
110     {
111         WINE_TRACE("Value not present - using default\n");
112         buffer = def ? strdupA(def) : NULL;
113         goto end;
114     } else if (res != ERROR_SUCCESS)
115     {
116         WINE_ERR("Couldn't query value's length (res=%ld)\n", res);
117         goto end;
118     }
119
120     buffer = HeapAlloc(GetProcessHeap(), 0, len + 1);
121
122     RegQueryValueEx(hSubKey, name, NULL, NULL, (LPBYTE) buffer, &len);
123
124     WINE_TRACE("buffer=%s\n", buffer);
125 end:
126     if (hSubKey && hSubKey != root) RegCloseKey(hSubKey);
127
128     return buffer;
129 }
130
131 /**
132  * set_config_key: convenience wrapper to set a key/value pair
133  *
134  * const char *subKey : the name of the config section
135  * const char *valueName : the name of the config value
136  * const char *value : the value to set the configuration key to
137  *
138  * Returns 0 on success, non-zero otherwise
139  *
140  * If valueName or value is NULL, an empty section will be created
141  */
142 static int set_config_key(HKEY root, const char *subkey, const char *name, const void *value, DWORD type) {
143     DWORD res = 1;
144     HKEY key = NULL;
145
146     WINE_TRACE("subkey=%s: name=%s, value=%p, type=%ld\n", subkey, name, value, type);
147
148     assert( subkey != NULL );
149
150     if (subkey[0])
151     {
152         res = RegCreateKey(root, subkey, &key);
153         if (res != ERROR_SUCCESS) goto end;
154     }
155     else key = root;
156     if (name == NULL || value == NULL) goto end;
157
158     switch (type)
159     {
160         case REG_SZ: res = RegSetValueEx(key, name, 0, REG_SZ, value, strlen(value) + 1); break;
161         case REG_DWORD: res = RegSetValueEx(key, name, 0, REG_DWORD, value, sizeof(DWORD)); break;
162     }
163     if (res != ERROR_SUCCESS) goto end;
164
165     res = 0;
166 end:
167     if (key && key != root) RegCloseKey(key);
168     if (res != 0) WINE_ERR("Unable to set configuration key %s in section %s, res=%ld\n", name, subkey, res);
169     return res;
170 }
171
172 /* removes the requested value from the registry, however, does not
173  * remove the section if empty. Returns S_OK (0) on success.
174  */
175 static HRESULT remove_value(HKEY root, const char *subkey, const char *name)
176 {
177     HRESULT hr;
178     HKEY key;
179
180     WINE_TRACE("subkey=%s, name=%s\n", subkey, name);
181
182     hr = RegOpenKey(root, subkey, &key);
183     if (hr != S_OK) return hr;
184
185     hr = RegDeleteValue(key, name);
186     if (hr != ERROR_SUCCESS) return hr;
187
188     return S_OK;
189 }
190
191 /* removes the requested subkey from the registry, assuming it exists */
192 static HRESULT remove_path(HKEY root, char *section) {
193     WINE_TRACE("section=%s\n", section);
194
195     return RegDeleteKey(root, section);
196 }
197
198
199 /* ========================================================================= */
200
201 /* This code exists for the following reasons:
202  *
203  * - It makes working with the registry easier
204  * - By storing a mini cache of the registry, we can more easily implement
205  *   cancel/revert and apply. The 'settings list' is an overlay on top of
206  *   the actual registry data that we can write out at will.
207  *
208  * Rather than model a tree in memory, we simply store each absolute (rooted
209  * at the config key) path.
210  *
211  */
212
213 struct setting
214 {
215     struct list entry;
216     HKEY root;    /* the key on which path is rooted */
217     char *path;   /* path in the registry rooted at root  */
218     char *name;   /* name of the registry value. if null, this means delete the key  */
219     char *value;  /* contents of the registry value. if null, this means delete the value  */
220     DWORD type;   /* type of registry value. REG_SZ or REG_DWORD for now */
221 };
222
223 struct list *settings;
224
225 static void free_setting(struct setting *setting)
226 {
227     assert( setting != NULL );
228     assert( setting->path );
229
230     WINE_TRACE("destroying %p: %s\n", setting, setting->path);
231     
232     HeapFree(GetProcessHeap(), 0, setting->path);
233     HeapFree(GetProcessHeap(), 0, setting->name);
234     HeapFree(GetProcessHeap(), 0, setting->value);
235
236     list_remove(&setting->entry);
237
238     HeapFree(GetProcessHeap(), 0, setting);
239 }
240
241 /**
242  * Returns the contents of the value at path. If not in the settings
243  * list, it will be fetched from the registry - failing that, the
244  * default will be used.
245  *
246  * If already in the list, the contents as given there will be
247  * returned. You are expected to HeapFree the result.
248  */
249 char *get_reg_key(HKEY root, const char *path, const char *name, const char *def)
250 {
251     struct list *cursor;
252     struct setting *s;
253     char *val;
254
255     WINE_TRACE("path=%s, name=%s, def=%s\n", path, name, def);
256
257     /* check if it's in the list */
258     LIST_FOR_EACH( cursor, settings )
259     {
260         s = LIST_ENTRY(cursor, struct setting, entry);
261
262         if (root != s->root) continue;
263         if (strcasecmp(path, s->path) != 0) continue;
264         if (strcasecmp(name, s->name) != 0) continue;
265
266         WINE_TRACE("found %s:%s in settings list, returning %s\n", path, name, s->value);
267         return s->value ? strdupA(s->value) : NULL;
268     }
269
270     /* no, so get from the registry */
271     val = get_config_key(root, path, name, def);
272
273     WINE_TRACE("returning %s\n", val);
274
275     return val;
276 }
277
278 /**
279  * Used to set a registry key.
280  *
281  * path is rooted at the config key, ie use "Version" or
282  * "AppDefaults\\fooapp.exe\\Version". You can use keypath()
283  * to get such a string.
284  *
285  * name is the value name, or NULL to delete the path.
286  *
287  * value is what to set the value to, or NULL to delete it.
288  *
289  * type is REG_SZ or REG_DWORD.
290  *
291  * These values will be copied when necessary.
292  */
293 static void set_reg_key_ex(HKEY root, const char *path, const char *name, const void *value, DWORD type)
294 {
295     struct list *cursor;
296     struct setting *s;
297
298     assert( path != NULL );
299
300     WINE_TRACE("path=%s, name=%s, value=%p\n", path, name, value);
301
302     /* firstly, see if we already set this setting  */
303     LIST_FOR_EACH( cursor, settings )
304     {
305         struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
306
307         if (root != s->root) continue;
308         if (strcasecmp(s->path, path) != 0) continue;
309         if ((s->name && name) && strcasecmp(s->name, name) != 0) continue;
310
311         /* are we attempting a double delete? */
312         if (!s->name && !name) return;
313
314         /* do we want to undelete this key? */
315         if (!s->name && name) s->name = strdupA(name);
316
317         /* yes, we have already set it, so just replace the content and return  */
318         HeapFree(GetProcessHeap(), 0, s->value);
319         s->type = type;
320         switch (type)
321         {
322             case REG_SZ:
323                 s->value = value ? strdupA(value) : NULL;
324                 break;
325             case REG_DWORD:
326                 s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
327                 memcpy( s->value, value, sizeof(DWORD) );
328                 break;
329         }
330
331         /* are we deleting this key? this won't remove any of the
332          * children from the overlay so if the user adds it again in
333          * that session it will appear to undelete the settings, but
334          * in reality only the settings actually modified by the user
335          * in that session will be restored. we might want to fix this
336          * corner case in future by actually deleting all the children
337          * here so that once it's gone, it's gone.
338          */
339         if (!name) s->name = NULL;
340
341         return;
342     }
343
344     /* otherwise add a new setting for it  */
345     s = HeapAlloc(GetProcessHeap(), 0, sizeof(struct setting));
346     s->root  = root;
347     s->path  = strdupA(path);
348     s->name  = name  ? strdupA(name)  : NULL;
349     s->type  = type;
350     switch (type)
351     {
352         case REG_SZ:
353             s->value = value ? strdupA(value) : NULL;
354             break;
355         case REG_DWORD:
356             s->value = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
357             memcpy( s->value, value, sizeof(DWORD) );
358             break;
359     }
360
361     list_add_tail(settings, &s->entry);
362 }
363
364 void set_reg_key(HKEY root, const char *path, const char *name, const char *value)
365 {
366     set_reg_key_ex(root, path, name, value, REG_SZ);
367 }
368
369 void set_reg_key_dword(HKEY root, const char *path, const char *name, DWORD value)
370 {
371     set_reg_key_ex(root, path, name, &value, REG_DWORD);
372 }
373
374 /**
375  * enumerates the value names at the given path, taking into account
376  * the changes in the settings list.
377  *
378  * you are expected to HeapFree each element of the array, which is null
379  * terminated, as well as the array itself.
380  */
381 char **enumerate_values(HKEY root, char *path)
382 {
383     HKEY key;
384     DWORD res, i = 0;
385     char **values = NULL;
386     int valueslen = 0;
387     struct list *cursor;
388
389     res = RegOpenKey(root, path, &key);
390     if (res == ERROR_SUCCESS)
391     {
392         while (TRUE)
393         {
394             char name[1024];
395             DWORD namesize = sizeof(name);
396             BOOL removed = FALSE;
397
398             /* find out the needed size, allocate a buffer, read the value  */
399             if ((res = RegEnumValue(key, i, name, &namesize, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
400                 break;
401
402             WINE_TRACE("name=%s\n", name);
403
404             /* check if this value name has been removed in the settings list  */
405             LIST_FOR_EACH( cursor, settings )
406             {
407                 struct setting *s = LIST_ENTRY(cursor, struct setting, entry);
408                 if (strcasecmp(s->path, path) != 0) continue;
409                 if (strcasecmp(s->name, name) != 0) continue;
410
411                 if (!s->value)
412                 {
413                     WINE_TRACE("this key has been removed, so skipping\n");
414                     removed = TRUE;
415                     break;
416                 }
417             }
418
419             if (removed)            /* this value was deleted by the user, so don't include it */
420             {
421                 HeapFree(GetProcessHeap(), 0, name);
422                 i++;
423                 continue;
424             }
425
426             /* grow the array if necessary, add buffer to it, iterate  */
427             if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
428             else values = HeapAlloc(GetProcessHeap(), 0, sizeof(char*));
429
430             values[valueslen++] = strdupA(name);
431             WINE_TRACE("valueslen is now %d\n", valueslen);
432             i++;
433         }
434     }
435     else
436     {
437         WINE_WARN("failed opening registry key %s, res=0x%lx\n", path, res);
438     }
439
440     WINE_TRACE("adding settings in list but not registry\n");
441
442     /* now we have to add the values that aren't in the registry but are in the settings list */
443     LIST_FOR_EACH( cursor, settings )
444     {
445         struct setting *setting = LIST_ENTRY(cursor, struct setting, entry);
446         BOOL found = FALSE;
447
448         if (strcasecmp(setting->path, path) != 0) continue;
449
450         if (!setting->value) continue;
451
452         for (i = 0; i < valueslen; i++)
453         {
454             if (strcasecmp(setting->name, values[i]) == 0)
455             {
456                 found = TRUE;
457                 break;
458             }
459         }
460
461         if (found) continue;
462
463         WINE_TRACE("%s in list but not registry\n", setting->name);
464
465         /* otherwise it's been set by the user but isn't in the registry */
466         if (values) values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
467         else values = HeapAlloc(GetProcessHeap(), 0, sizeof(char*));
468
469         values[valueslen++] = strdupA(setting->name);
470     }
471
472     WINE_TRACE("adding null terminator\n");
473     if (values)
474     {
475         values = HeapReAlloc(GetProcessHeap(), 0, values, sizeof(char*) * (valueslen + 1));
476         values[valueslen] = NULL;
477     }
478
479     RegCloseKey(key);
480
481     return values;
482 }
483
484 /**
485  * returns true if the given key/value pair exists in the registry or
486  * has been written to.
487  */
488 BOOL reg_key_exists(HKEY root, const char *path, const char *name)
489 {
490     char *val = get_reg_key(root, path, name, NULL);
491
492     if (val)
493     {
494         HeapFree(GetProcessHeap(), 0, val);
495         return TRUE;
496     }
497
498     return FALSE;
499 }
500
501 static void process_setting(struct setting *s)
502 {
503     if (s->value)
504     {
505         WINE_TRACE("Setting %s:%s to '%s'\n", s->path, s->name, s->value);
506         set_config_key(s->root, s->path, s->name, s->value, s->type);
507     }
508     else
509     {
510         /* NULL name means remove that path/section entirely */
511         if (s->path && s->name) remove_value(s->root, s->path, s->name);
512         else if (s->path && !s->name) remove_path(s->root, s->path);
513     }
514 }
515
516 void apply(void)
517 {
518     if (list_empty(settings)) return; /* we will be called for each page when the user clicks OK */
519
520     WINE_TRACE("()\n");
521
522     while (!list_empty(settings))
523     {
524         struct setting *s = (struct setting *) list_head(settings);
525         process_setting(s);
526         free_setting(s);
527     }
528 }
529
530 /* ================================== utility functions ============================ */
531
532 char *current_app = NULL; /* the app we are currently editing, or NULL if editing global */
533
534 /* returns a registry key path suitable for passing to addTransaction  */
535 char *keypath(const char *section)
536 {
537     static char *result = NULL;
538
539     HeapFree(GetProcessHeap(), 0, result);
540
541     if (current_app)
542     {
543         result = HeapAlloc(GetProcessHeap(), 0, strlen("AppDefaults\\") + strlen(current_app) + 2 /* \\ */ + strlen(section) + 1 /* terminator */);
544         sprintf(result, "AppDefaults\\%s", current_app);
545         if (section[0]) sprintf( result + strlen(result), "\\%s", section );
546     }
547     else
548     {
549         result = strdupA(section);
550     }
551
552     return result;
553 }
554
555 void PRINTERROR(void)
556 {
557         LPSTR msg;
558
559         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
560                        0, GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
561                        (LPSTR)&msg, 0, NULL);
562
563         /* eliminate trailing newline, is this a Wine bug? */
564         *(strrchr(msg, '\r')) = '\0';
565         
566         WINE_TRACE("error: '%s'\n", msg);
567 }
568
569 int initialize(void) {
570     DWORD res = RegCreateKey(HKEY_CURRENT_USER, WINE_KEY_ROOT, &config_key);
571
572     if (res != ERROR_SUCCESS) {
573         WINE_ERR("RegOpenKey failed on wine config key (%ld)\n", res);
574         return 1;
575     }
576
577     /* we could probably just have the list as static data  */
578     settings = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
579     list_init(settings);
580
581     return 0;
582 }