xref: /reactos/base/applications/regedit/regedit.c (revision 84344399)
1 /*
2  * Windows regedit.exe registry editor implementation.
3  *
4  * Copyright 2002 Andriy Palamarchuk
5  * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
6  */
7 
8 #ifndef __REACTOS__
9 #include <stdlib.h>
10 #include <windows.h>
11 #include <commctrl.h>
12 #include <shellapi.h>
13 
14 #include "wine/debug.h"
15 #include "main.h"
16 #else
17 #include "regedit.h"
18 #endif
19 
20 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
21 
22 static void output_writeconsole(const WCHAR *str, DWORD wlen)
23 {
24 #ifdef __REACTOS__
25     /* This is win32gui application, don't ever try writing to console.
26      * For the console version we have a separate reg.exe application. */
27     MessageBoxW(NULL, str, NULL, MB_ICONERROR);
28 #else
29     DWORD count;
30 
31     if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL))
32     {
33         DWORD len;
34         char  *msgA;
35 
36         /* WriteConsole() fails on Windows if its output is redirected. If this occurs,
37          * we should call WriteFile() with OEM code page.
38          */
39         len = WideCharToMultiByte(GetOEMCP(), 0, str, wlen, NULL, 0, NULL, NULL);
40         msgA = malloc(len);
41         if (!msgA) return;
42 
43         WideCharToMultiByte(GetOEMCP(), 0, str, wlen, msgA, len, NULL, NULL);
44         WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
45         free(msgA);
46     }
47 #endif
48 }
49 
50 static void output_formatstring(const WCHAR *fmt, va_list va_args)
51 {
52     WCHAR *str;
53     DWORD len;
54 
55 #ifdef __REACTOS__
56     SetLastError(NO_ERROR);
57 #endif
58     len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
59                          fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
60 #ifdef __REACTOS__
61     if (len == 0 && GetLastError() != NO_ERROR)
62 #else
63     if (len == 0 && GetLastError() != ERROR_NO_WORK_DONE)
64 #endif
65     {
66         WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
67         return;
68     }
69     output_writeconsole(str, len);
70     LocalFree(str);
71 }
72 
73 void WINAPIV output_message(unsigned int id, ...)
74 {
75     WCHAR fmt[1536];
76     va_list va_args;
77 
78     if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
79     {
80         WINE_FIXME("LoadString failed with %ld\n", GetLastError());
81         return;
82     }
83     va_start(va_args, id);
84     output_formatstring(fmt, va_args);
85     va_end(va_args);
86 }
87 
88 void WINAPIV error_exit(unsigned int id, ...)
89 {
90     WCHAR fmt[1536];
91     va_list va_args;
92 
93     if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
94     {
95 #ifndef __REACTOS__
96         WINE_FIXME("LoadString failed with %lu\n", GetLastError());
97 #endif
98         return;
99     }
100     va_start(va_args, id);
101     output_formatstring(fmt, va_args);
102     va_end(va_args);
103 
104     exit(0); /* regedit.exe always terminates with error code zero */
105 }
106 
107 typedef enum {
108     ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
109 } REGEDIT_ACTION;
110 
111 #ifdef __REACTOS__
112 static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i, BOOL silent)
113 #else
114 static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i)
115 #endif
116 {
117     switch (action) {
118     case ACTION_ADD: {
119             WCHAR *filename = argv[*i];
120             WCHAR *realname = NULL;
121             FILE *reg_file;
122 
123 #ifdef __REACTOS__
124             /* Request import confirmation */
125             if (!silent)
126             {
127                 WCHAR szText[512];
128                 int choice;
129                 UINT mbType = MB_YESNO;
130 
131                 LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, ARRAY_SIZE(szText));
132 
133                 if (argv[*i + 1] != NULL)
134                 {
135                     /* Enable three buttons if there's another file coming */
136                     mbType = MB_YESNOCANCEL;
137                 }
138 
139                 choice = InfoMessageBox(NULL, mbType | MB_ICONQUESTION, szTitle, szText, filename);
140                 switch (choice)
141                 {
142                     case IDNO:
143                         return;
144                     case IDCANCEL:
145                         /* The cancel case is useful if the user is importing more than one registry file
146                          * at a time, and wants to back out anytime during the import process. This way, the
147                          * user doesn't have to resort to ending the regedit process abruptly just to cancel
148                          * the operation.
149                          * To achieve this, we skip all further command line arguments.
150                          */
151                         *i = INT_MAX - 1;
152                         return;
153                     default:
154                         break;
155                 }
156             }
157 #endif
158             if (!lstrcmpW(filename, L"-"))
159                 reg_file = stdin;
160             else
161             {
162                 int size;
163 
164                 size = SearchPathW(NULL, filename, NULL, 0, NULL, NULL);
165                 if (size > 0)
166                 {
167                     realname = malloc(size * sizeof(WCHAR));
168                     size = SearchPathW(NULL, filename, NULL, size, realname, NULL);
169                 }
170                 if (size == 0)
171                 {
172                     output_message(STRING_FILE_NOT_FOUND, filename);
173                     free(realname);
174                     return;
175                 }
176                 reg_file = _wfopen(realname, L"rb");
177                 if (reg_file == NULL)
178                 {
179                     _wperror(L"regedit");
180                     output_message(STRING_CANNOT_OPEN_FILE, filename);
181                     free(realname);
182                     return;
183                 }
184             }
185             import_registry_file(reg_file);
186             if (realname)
187             {
188                 free(realname);
189                 fclose(reg_file);
190             }
191             break;
192         }
193     case ACTION_DELETE:
194             delete_registry_key(argv[*i]);
195             break;
196     case ACTION_EXPORT: {
197             WCHAR *filename = argv[*i];
198             WCHAR *key_name = argv[++(*i)];
199 
200             if (key_name && *key_name)
201                 export_registry_key(filename, key_name, REG_FORMAT_5);
202             else
203                 export_registry_key(filename, NULL, REG_FORMAT_5);
204             break;
205         }
206     default:
207 #ifdef __REACTOS__
208         output_message(STRING_UNHANDLED_ACTION);
209 #else
210         error_exit(STRING_UNHANDLED_ACTION);
211 #endif
212         break;
213     }
214 }
215 
216 BOOL ProcessCmdLine(WCHAR *cmdline)
217 {
218     WCHAR **argv;
219     int argc, i;
220     REGEDIT_ACTION action = ACTION_ADD;
221 #ifdef __REACTOS__
222     BOOL silent = FALSE;
223 #endif
224 
225     argv = CommandLineToArgvW(cmdline, &argc);
226 
227     if (!argv)
228         return FALSE;
229 
230     if (argc == 1)
231     {
232         LocalFree(argv);
233         return FALSE;
234     }
235 
236     for (i = 1; i < argc; i++)
237     {
238         if (argv[i][0] != '/' && argv[i][0] != '-')
239             break; /* No flags specified. */
240 
241         if (!argv[i][1] && argv[i][0] == '-')
242             break; /* '-' is a filename. It indicates we should use stdin. */
243 
244         if (argv[i][1] && argv[i][2] && argv[i][2] != ':')
245             break; /* This is a file path beginning with '/'. */
246 
247         switch (towupper(argv[i][1]))
248         {
249         case '?':
250             error_exit(STRING_USAGE);
251             break;
252         case 'D':
253             action = ACTION_DELETE;
254             break;
255         case 'E':
256             action = ACTION_EXPORT;
257             break;
258         case 'C':
259         case 'L':
260         case 'M':
261         case 'R':
262             /* unhandled */;
263             break;
264         case 'S':
265 #ifdef __REACTOS__
266             silent = TRUE;
267             break;
268 #endif
269         case 'V':
270             /* ignored */;
271             break;
272         default:
273             output_message(STRING_INVALID_SWITCH, argv[i]);
274             error_exit(STRING_HELP);
275         }
276     }
277 
278     if (i == argc)
279     {
280         switch (action)
281         {
282         case ACTION_ADD:
283         case ACTION_EXPORT:
284             output_message(STRING_NO_FILENAME);
285             break;
286         case ACTION_DELETE:
287             output_message(STRING_NO_REG_KEY);
288             break;
289         }
290         error_exit(STRING_HELP);
291     }
292 
293     for (; i < argc; i++)
294 #ifdef __REACTOS__
295         PerformRegAction(action, argv, &i, silent);
296 #else
297         PerformRegAction(action, argv, &i);
298 #endif
299 
300     LocalFree(argv);
301 
302     return TRUE;
303 }
304