Update German resource files.
[wine] / programs / regedit / edit.c
1 /*
2  * Registry editing UI functions.
3  *
4  * Copyright (C) 2003 Dimitrie O. Paun
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define WIN32_LEAN_AND_MEAN     /* Exclude rarely-used stuff from Windows headers */
22
23 #include <windows.h>
24 #include <tchar.h>
25 #include <commctrl.h>
26 #include <commdlg.h>
27 #include <cderr.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <shellapi.h>
31 #include <shlwapi.h>
32
33 #include "main.h"
34 #include "regproc.h"
35 #include "resource.h"
36
37 static const TCHAR* editValueName;
38 static TCHAR* stringValueData;
39 static BOOL isDecimal;
40
41 struct edit_params
42 {
43     HKEY    hKey;
44     LPCTSTR lpszValueName;
45     void   *pData;
46     LONG    cbData;
47 };
48
49 INT vmessagebox(HWND hwnd, INT buttons, INT titleId, INT resId, va_list ap)
50 {
51     TCHAR title[256];
52     TCHAR errfmt[1024];
53     TCHAR errstr[1024];
54
55     if (!LoadString(hInst, titleId, title, COUNT_OF(title)))
56         lstrcpy(title, "Error");
57
58     if (!LoadString(hInst, resId, errfmt, COUNT_OF(errfmt)))
59         lstrcpy(errfmt, "Unknown error string!");
60
61     _vsntprintf(errstr, COUNT_OF(errstr), errfmt, ap);
62
63     return MessageBox(hwnd, errstr, title, buttons);
64 }
65
66 INT messagebox(HWND hwnd, INT buttons, INT titleId, INT resId, ...)
67 {
68     va_list ap;
69     INT result;
70
71     va_start(ap, resId);
72     result = vmessagebox(hwnd, buttons, titleId, resId, ap);
73     va_end(ap);
74
75     return result;
76 }
77
78 void error(HWND hwnd, INT resId, ...)
79 {
80     va_list ap;
81
82     va_start(ap, resId);
83     vmessagebox(hwnd, MB_OK | MB_ICONERROR, IDS_ERROR, resId, ap);
84     va_end(ap);
85 }
86
87 static void error_code_messagebox(HWND hwnd, DWORD error_code)
88 {
89     LPTSTR lpMsgBuf;
90     DWORD status;
91     TCHAR title[256];
92     static const TCHAR fallback[] = TEXT("Error displaying error message.\n");
93     if (!LoadString(hInst, IDS_ERROR, title, COUNT_OF(title)))
94         lstrcpy(title, TEXT("Error"));
95     status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
96                            NULL, error_code, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
97     if (!status)
98         lpMsgBuf = (LPTSTR)fallback;
99     MessageBox(hwnd, lpMsgBuf, title, MB_OK | MB_ICONERROR);
100     if (lpMsgBuf != fallback)
101         LocalFree(lpMsgBuf);
102 }
103
104 BOOL change_dword_base(HWND hwndDlg, BOOL toHex)
105 {
106     TCHAR buf[128];
107     DWORD val;
108
109     if (!GetDlgItemText(hwndDlg, IDC_VALUE_DATA, buf, COUNT_OF(buf))) return FALSE;
110     if (!_stscanf(buf, toHex ? "%ld" : "%lx", &val)) return FALSE;
111     wsprintf(buf, toHex ? "%lx" : "%ld", val);
112     return SetDlgItemText(hwndDlg, IDC_VALUE_DATA, buf);    
113 }
114
115 INT_PTR CALLBACK modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
116 {
117     TCHAR* valueData;
118     HWND hwndValue;
119     int len;
120
121     switch(uMsg) {
122     case WM_INITDIALOG:
123         SetDlgItemText(hwndDlg, IDC_VALUE_NAME, editValueName);
124         SetDlgItemText(hwndDlg, IDC_VALUE_DATA, stringValueData);
125         CheckRadioButton(hwndDlg, IDC_DWORD_HEX, IDC_DWORD_DEC, isDecimal ? IDC_DWORD_DEC : IDC_DWORD_HEX);
126         return TRUE;
127     case WM_COMMAND:
128         switch (LOWORD(wParam)) {
129         case IDC_DWORD_HEX:
130             if (isDecimal && change_dword_base(hwndDlg, TRUE)) isDecimal = FALSE;
131         break;
132         case IDC_DWORD_DEC:
133             if (!isDecimal && change_dword_base(hwndDlg, FALSE)) isDecimal = TRUE;
134         break;
135         case IDOK:
136             if ((hwndValue = GetDlgItem(hwndDlg, IDC_VALUE_DATA))) {
137                 len = GetWindowTextLength(hwndValue);
138                 if ((valueData = HeapReAlloc(GetProcessHeap(), 0, stringValueData, (len + 1) * sizeof(TCHAR)))) {
139                     stringValueData = valueData;
140                     if (!GetWindowText(hwndValue, stringValueData, len + 1))
141                         *stringValueData = 0;
142                 }
143             }
144             /* Fall through */
145         case IDCANCEL:
146             EndDialog(hwndDlg, wParam);
147             return TRUE;
148         }
149     }
150     return FALSE;
151 }
152
153 static INT_PTR CALLBACK bin_modify_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
154 {
155     struct edit_params *params;
156     LPBYTE pData;
157     LONG cbData;
158     LONG lRet;
159
160     switch(uMsg) {
161     case WM_INITDIALOG:
162         params = (struct edit_params *)lParam;
163         SetWindowLongPtr(hwndDlg, DWLP_USER, (ULONG_PTR)params);
164         if (params->lpszValueName)
165             SetDlgItemText(hwndDlg, IDC_VALUE_NAME, params->lpszValueName);
166         else
167             SetDlgItemText(hwndDlg, IDC_VALUE_NAME, g_pszDefaultValueName);
168         SendDlgItemMessage(hwndDlg, IDC_VALUE_DATA, HEM_SETDATA, (WPARAM)params->cbData, (LPARAM)params->pData);
169         return TRUE;
170     case WM_COMMAND:
171         switch (LOWORD(wParam)) {
172         case IDOK:
173             params = (struct edit_params *)GetWindowLongPtr(hwndDlg, DWLP_USER);
174             cbData = SendDlgItemMessage(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, 0, 0);
175             pData = HeapAlloc(GetProcessHeap(), 0, cbData);
176
177             if (pData)
178             {
179                 SendDlgItemMessage(hwndDlg, IDC_VALUE_DATA, HEM_GETDATA, (WPARAM)cbData, (LPARAM)pData);
180                 lRet = RegSetValueEx(params->hKey, params->lpszValueName, 0, REG_BINARY, pData, cbData);
181             }
182             else
183                 lRet = ERROR_OUTOFMEMORY;
184
185             if (lRet == ERROR_SUCCESS)
186                 EndDialog(hwndDlg, 1);
187             else
188             {
189                 error_code_messagebox(hwndDlg, lRet);
190                 EndDialog(hwndDlg, 0);
191             }
192             return TRUE;
193         case IDCANCEL:
194             EndDialog(hwndDlg, 0);
195             return TRUE;
196         }
197     }
198     return FALSE;
199 }
200
201 static BOOL check_value(HWND hwnd, HKEY hKey, LPCTSTR valueName)
202 {
203     LONG lRet = RegQueryValueEx(hKey, valueName ? valueName : _T(""), 0, NULL, 0, NULL);
204     if(lRet != ERROR_SUCCESS) return FALSE;
205     return TRUE;
206 }
207
208 static LPTSTR read_value(HWND hwnd, HKEY hKey, LPCTSTR valueName, DWORD *lpType, LONG *len)
209 {
210     DWORD valueDataLen;
211     LPTSTR buffer = NULL;
212     LONG lRet;
213
214     lRet = RegQueryValueEx(hKey, valueName ? valueName : _T(""), 0, lpType, 0, &valueDataLen);
215     if (lRet != ERROR_SUCCESS) {
216         if (lRet == ERROR_FILE_NOT_FOUND && !valueName) { /* no default value here, make it up */
217             if (len) *len = 1;
218             if (lpType) *lpType = REG_SZ;
219             buffer = HeapAlloc(GetProcessHeap(), 0, 1);
220             *buffer = '\0';
221             return buffer;
222         }
223         error(hwnd, IDS_BAD_VALUE, valueName);
224         goto done;
225     }
226     if ( *lpType == REG_DWORD ) valueDataLen = sizeof(DWORD);
227     if (!(buffer = HeapAlloc(GetProcessHeap(), 0, valueDataLen))) {
228         error(hwnd, IDS_TOO_BIG_VALUE, valueDataLen);
229         goto done;
230     }
231     lRet = RegQueryValueEx(hKey, valueName, 0, 0, buffer, &valueDataLen);
232     if (lRet != ERROR_SUCCESS) {
233         error(hwnd, IDS_BAD_VALUE, valueName);
234         goto done;
235     }
236
237     if(len) *len = valueDataLen;
238     return buffer;
239
240 done:
241     HeapFree(GetProcessHeap(), 0, buffer);
242     return NULL;
243 }
244
245 BOOL CreateKey(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath, LPTSTR keyName)
246 {
247     BOOL result = FALSE;
248     LONG lRet = ERROR_SUCCESS;
249     HKEY retKey;
250     TCHAR newKey[MAX_NEW_KEY_LEN - 4];
251     int keyNum;
252     HKEY hKey;
253          
254     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_CREATE_SUB_KEY, &hKey);
255     if (lRet != ERROR_SUCCESS) {
256         error_code_messagebox(hwnd, lRet);
257         goto done;
258     }
259
260     if (!LoadString(GetModuleHandle(0), IDS_NEWKEY, newKey, COUNT_OF(newKey))) goto done;
261
262     /* try to find out a name for the newly create key (max 100 times) */
263     for (keyNum = 1; keyNum < 100; keyNum++) {
264         wsprintf(keyName, newKey, keyNum);
265         lRet = RegOpenKey(hKey, keyName, &retKey);
266         if (lRet != ERROR_SUCCESS) break;
267         RegCloseKey(retKey);
268     }
269     if (lRet == ERROR_SUCCESS) goto done;
270     
271     lRet = RegCreateKey(hKey, keyName, &retKey);
272     if (lRet != ERROR_SUCCESS) {
273         error_code_messagebox(hwnd, lRet);
274         goto done;
275     }
276
277     result = TRUE;
278
279 done:
280     RegCloseKey(retKey);
281     return result;
282 }
283
284 BOOL ModifyValue(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR valueName)
285 {
286     BOOL result = FALSE;
287     DWORD type;
288     LONG lRet;
289     HKEY hKey;
290     LONG len;
291
292     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
293     if (lRet != ERROR_SUCCESS) {
294         error_code_messagebox(hwnd, lRet);
295         return FALSE;
296     }
297
298     editValueName = valueName ? valueName : g_pszDefaultValueName;
299     if(!(stringValueData = read_value(hwnd, hKey, valueName, &type, &len))) goto done;
300
301     if ( (type == REG_SZ) || (type == REG_EXPAND_SZ) ) {
302         if (DialogBox(0, MAKEINTRESOURCE(IDD_EDIT_STRING), hwnd, modify_dlgproc) == IDOK) {
303             lRet = RegSetValueEx(hKey, valueName, 0, type, stringValueData, lstrlen(stringValueData) + 1);
304             if (lRet == ERROR_SUCCESS) result = TRUE;
305             else error_code_messagebox(hwnd, lRet);
306         }
307     } else if ( type == REG_DWORD ) {
308         wsprintf(stringValueData, isDecimal ? "%ld" : "%lx", *((DWORD*)stringValueData));
309         if (DialogBox(0, MAKEINTRESOURCE(IDD_EDIT_DWORD), hwnd, modify_dlgproc) == IDOK) {
310             DWORD val;
311             if (_stscanf(stringValueData, isDecimal ? "%ld" : "%lx", &val)) {
312                 lRet = RegSetValueEx(hKey, valueName, 0, type, (BYTE*)&val, sizeof(val));
313                 if (lRet == ERROR_SUCCESS) result = TRUE;
314                 else error_code_messagebox(hwnd, lRet);
315             }
316         }
317     } else if ( type == REG_BINARY ) {
318         struct edit_params params;
319         params.hKey = hKey;
320         params.lpszValueName = valueName;
321         params.pData = stringValueData;
322         params.cbData = len;
323         result = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_EDIT_BINARY), hwnd,
324             bin_modify_dlgproc, (LPARAM)&params);
325     } else {
326         error(hwnd, IDS_UNSUPPORTED_TYPE, type);
327     }
328
329 done:
330     HeapFree(GetProcessHeap(), 0, stringValueData);
331     stringValueData = NULL;
332     RegCloseKey(hKey);
333     return result;
334 }
335
336 BOOL DeleteKey(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath)
337 {
338     BOOL result = FALSE;
339     LONG lRet;
340     HKEY hKey;
341     
342     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ|KEY_SET_VALUE, &hKey);
343     if (lRet != ERROR_SUCCESS) {
344         error_code_messagebox(hwnd, lRet);
345         return FALSE;
346     }
347     
348     if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_BOX_TITLE, IDS_DELETE_BOX_TEXT, keyPath) != IDYES)
349         goto done;
350         
351     lRet = SHDeleteKey(hKeyRoot, keyPath);
352     if (lRet != ERROR_SUCCESS) {
353         error(hwnd, IDS_BAD_KEY, keyPath);
354         goto done;
355     }
356     result = TRUE;
357     
358 done:
359     RegCloseKey(hKey);
360     return result;
361 }
362
363 BOOL DeleteValue(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR valueName)
364 {
365     BOOL result = FALSE;
366     LONG lRet;
367     HKEY hKey;
368     LPCSTR visibleValueName = valueName ? valueName : g_pszDefaultValueName;
369
370     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
371     if (lRet != ERROR_SUCCESS) return FALSE;
372
373     if (messagebox(hwnd, MB_YESNO | MB_ICONEXCLAMATION, IDS_DELETE_BOX_TITLE, IDS_DELETE_BOX_TEXT, visibleValueName) != IDYES)
374         goto done;
375
376     lRet = RegDeleteValue(hKey, valueName ? valueName : "");
377     if (lRet != ERROR_SUCCESS && valueName) {
378         error(hwnd, IDS_BAD_VALUE, valueName);
379     }
380     if (lRet != ERROR_SUCCESS) goto done;
381     result = TRUE;
382
383 done:
384     RegCloseKey(hKey);
385     return result;
386 }
387
388 BOOL CreateValue(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath, DWORD valueType, LPTSTR valueName)
389 {
390     LONG lRet = ERROR_SUCCESS;
391     TCHAR newValue[256];
392     DWORD valueDword = 0;
393     BOOL result = FALSE;
394     int valueNum;
395     HKEY hKey;
396          
397     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
398     if (lRet != ERROR_SUCCESS) {
399         error_code_messagebox(hwnd, lRet);
400         return FALSE;
401     }
402
403     if (!LoadString(GetModuleHandle(0), IDS_NEWVALUE, newValue, COUNT_OF(newValue))) goto done;
404
405     /* try to find out a name for the newly create key (max 100 times) */
406     for (valueNum = 1; valueNum < 100; valueNum++) {
407         wsprintf(valueName, newValue, valueNum);
408         lRet = RegQueryValueEx(hKey, valueName, 0, 0, 0, 0);
409         if (lRet == ERROR_FILE_NOT_FOUND) break;
410     }
411     if (lRet != ERROR_FILE_NOT_FOUND) {
412         error_code_messagebox(hwnd, lRet);
413         goto done;
414     }
415    
416     lRet = RegSetValueEx(hKey, valueName, 0, valueType, (BYTE*)&valueDword, sizeof(DWORD));
417     if (lRet != ERROR_SUCCESS) {
418         error_code_messagebox(hwnd, lRet);
419         goto done;
420     }
421     result = TRUE;
422
423 done:
424     RegCloseKey(hKey);
425     return result;
426 }
427
428 BOOL RenameValue(HWND hwnd, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR oldName, LPCTSTR newName)
429 {
430     LPTSTR value = NULL;
431     DWORD type;
432     LONG len, lRet;
433     BOOL result = FALSE;
434     HKEY hKey;
435
436     if (!oldName) return FALSE;
437     if (!newName) return FALSE;
438
439     lRet = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ | KEY_SET_VALUE, &hKey);
440     if (lRet != ERROR_SUCCESS) {
441         error_code_messagebox(hwnd, lRet);
442         return FALSE;
443     }
444     /* check if value already exists */
445     if (check_value(hwnd, hKey, newName)) goto done;
446     value = read_value(hwnd, hKey, oldName, &type, &len);
447     if(!value) goto done;
448     lRet = RegSetValueEx(hKey, newName, 0, type, (BYTE*)value, len);
449     if (lRet != ERROR_SUCCESS) {
450         error_code_messagebox(hwnd, lRet);
451         goto done;
452     }
453     lRet = RegDeleteValue(hKey, oldName);
454     if (lRet != ERROR_SUCCESS) {
455         RegDeleteValue(hKey, newName);
456         error_code_messagebox(hwnd, lRet);
457         goto done;
458     }
459     result = TRUE;
460
461 done:
462     HeapFree(GetProcessHeap(), 0, value);
463     RegCloseKey(hKey);
464     return result;
465 }
466
467
468 BOOL RenameKey(HWND hwnd, HKEY hRootKey, LPCTSTR keyPath, LPCTSTR newName)
469 {
470     LPTSTR parentPath = 0;
471     LPCTSTR srcSubKey = 0;
472     HKEY parentKey = 0;
473     HKEY destKey = 0;
474     BOOL result = FALSE;
475     LONG lRet;
476     DWORD disposition;
477
478     if (!keyPath || !newName) return FALSE;
479
480     if (!strrchr(keyPath, '\\')) {
481         parentKey = hRootKey;
482         srcSubKey = keyPath;
483     } else {
484         parentPath = strdup(keyPath);
485         srcSubKey = strrchr(parentPath, '\\') + 1;
486         *((LPTSTR)srcSubKey - 1) = 0;
487         lRet = RegOpenKeyEx(hRootKey, parentPath, 0, KEY_READ | KEY_CREATE_SUB_KEY, &parentKey);
488         if (lRet != ERROR_SUCCESS) {
489             error_code_messagebox(hwnd, lRet);
490             goto done;
491         }
492     }
493
494     /* The following fails if the old name is the same as the new name. */
495     if (!strcmp(srcSubKey, newName)) goto done;
496
497     lRet = RegCreateKeyEx(parentKey, newName, 0, NULL, REG_OPTION_NON_VOLATILE,
498         KEY_WRITE, NULL /* FIXME */, &destKey, &disposition);
499     if (disposition == REG_OPENED_EXISTING_KEY)
500         lRet = ERROR_FILE_EXISTS; /* FIXME: we might want a better error message than this */
501     if (lRet != ERROR_SUCCESS) {
502         error_code_messagebox(hwnd, lRet);
503         goto done;
504     }
505
506     /* FIXME: SHCopyKey does not copy the security attributes */
507     lRet = SHCopyKey(parentKey, srcSubKey, destKey, 0);
508     if (lRet != ERROR_SUCCESS) {
509         RegCloseKey(destKey);
510         RegDeleteKey(parentKey, newName);
511         error_code_messagebox(hwnd, lRet);
512         goto done;
513     }
514
515     lRet = SHDeleteKey(hRootKey, keyPath);
516     if (lRet != ERROR_SUCCESS) {
517         error_code_messagebox(hwnd, lRet);
518         goto done;
519     }
520
521     result = TRUE;
522
523 done:
524     RegCloseKey(destKey);
525     if (parentKey) {
526         RegCloseKey(parentKey); 
527         free(parentPath);
528     }
529     return result;
530 }