Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / base / applications / regedit / regedit.c
1 /*
2 * Windows regedit.exe registry editor implementation.
3 *
4 * Copyright (C) 2002 Andriy Palamarchuk
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "regedit.h"
22
23 static const LPCWSTR usage =
24 L"Usage:\n"
25 L" regedit filenames\n"
26 L" regedit /E filename [regpath]\n"
27 L" regedit /D regpath\n"
28 L"\n"
29 L"filenames - List of registry files names\n"
30 L"filename - Registry file name\n"
31 L"regpath - Name of the registry key\n"
32 L"\n"
33 L"When is called without any switches adds contents of the specified\n"
34 L"registry files to the registry.\n"
35 L"\n"
36 L"Switches:\n"
37 L" /E - Exports contents of the specified registry key to the specified\n"
38 L" file. Exports the whole registry if no key is specified.\n"
39 L" /D - Deletes specified registry key\n"
40 L" /S - Silent execution, can be used with any other switch.\n"
41 L" The only existing mode, exists for compatibility with Windows regedit.\n"
42 L" /V - Advanced mode, can be used with any other switch.\n"
43 L" Ignored, exists for compatibility with Windows regedit.\n"
44 L" /L - Location of system.dat file. Can be used with any other switch.\n"
45 L" Ignored. Exists for compatibility with Windows regedit.\n"
46 L" /R - Location of user.dat file. Can be used with any other switch.\n"
47 L" Ignored. Exists for compatibility with Windows regedit.\n"
48 L" /? - Print this help. Any other switches are ignored.\n"
49 L" /C - Create registry from. Not implemented.\n"
50 L"\n"
51 L"The switches are case-insensitive, can be prefixed either by '-' or '/'.\n"
52 L"This program is command-line compatible with Microsoft Windows\n"
53 L"regedit.\n";
54
55 typedef enum
56 {
57 ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
58 } REGEDIT_ACTION;
59
60
61 LPCWSTR getAppName(void)
62 {
63 return L"regedit";
64 }
65
66 /******************************************************************************
67 * Copies file name from command line string to the buffer.
68 * Rewinds the command line string pointer to the next non-space character
69 * after the file name.
70 * Buffer contains an empty string if no filename was found;
71 *
72 * params:
73 * command_line - command line current position pointer
74 * where *s[0] is the first symbol of the file name.
75 * file_name - buffer to write the file name to.
76 */
77 void get_file_name(LPWSTR *command_line, LPWSTR file_name)
78 {
79 WCHAR *s = *command_line;
80 size_t pos = 0; /* position of pointer "s" in *command_line */
81 file_name[0] = 0;
82
83 if (!s[0])
84 {
85 return;
86 }
87
88 if (s[0] == L'"')
89 {
90 s++;
91 (*command_line)++;
92 while(s[0] != L'"')
93 {
94 if (!s[0])
95 {
96 fwprintf(stderr, L"%s: Unexpected end of file name!\n", getAppName());
97 exit(1);
98 }
99 s++;
100 pos++;
101 }
102 }
103 else
104 {
105 while(s[0] && !iswspace(s[0]))
106 {
107 s++;
108 pos++;
109 }
110 }
111 memcpy(file_name, *command_line, pos * sizeof(WCHAR));
112 /* remove the last backslash */
113 if (file_name[pos - 1] == L'\\')
114 {
115 file_name[pos - 1] = L'\0';
116 }
117 else
118 {
119 file_name[pos] = L'\0';
120 }
121
122 if (s[0])
123 {
124 s++;
125 pos++;
126 }
127 while(s[0] && iswspace(s[0]))
128 {
129 s++;
130 pos++;
131 }
132 (*command_line) += pos;
133 }
134
135 BOOL PerformRegAction(REGEDIT_ACTION action, LPWSTR s, BOOL silent)
136 {
137 switch (action)
138 {
139 case ACTION_ADD:
140 {
141 WCHAR szText[512];
142 WCHAR filename[MAX_PATH];
143 FILE *fp;
144
145 get_file_name(&s, filename);
146 if (!filename[0])
147 {
148 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified.");
149 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage);
150 exit(4);
151 }
152
153 while (filename[0])
154 {
155 /* Request import confirmation */
156 if (!silent)
157 {
158 int choice;
159
160 LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, COUNT_OF(szText));
161
162 choice = InfoMessageBox(NULL, MB_YESNOCANCEL | MB_ICONWARNING, szTitle, szText, filename);
163
164 switch (choice)
165 {
166 case IDNO:
167 goto cont;
168 case IDCANCEL:
169 /* The cancel case is useful if the user is importing more than one registry file
170 at a time, and wants to back out anytime during the import process. This way, the
171 user doesn't have to resort to ending the regedit process abruptly just to cancel
172 the operation. */
173 return TRUE;
174 default:
175 break;
176 }
177 }
178
179 /* Open the file */
180 fp = _wfopen(filename, L"r");
181
182 /* Import it */
183 if (fp == NULL || !import_registry_file(fp))
184 {
185 /* Error opening the file */
186 if (!silent)
187 {
188 LoadStringW(hInst, IDS_IMPORT_ERROR, szText, COUNT_OF(szText));
189 InfoMessageBox(NULL, MB_OK | MB_ICONERROR, szTitle, szText, filename);
190 }
191 }
192 else
193 {
194 /* Show successful import */
195 if (!silent)
196 {
197 LoadStringW(hInst, IDS_IMPORT_OK, szText, COUNT_OF(szText));
198 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, szText, filename);
199 }
200 }
201
202 /* Close the file */
203 if (fp) fclose(fp);
204
205 cont:
206 get_file_name(&s, filename);
207 }
208 break;
209 }
210
211 case ACTION_DELETE:
212 {
213 WCHAR reg_key_name[KEY_MAX_LEN];
214 get_file_name(&s, reg_key_name);
215 if (!reg_key_name[0])
216 {
217 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No registry key is specified for removal.");
218 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage);
219 exit(6);
220 }
221 delete_registry_key(reg_key_name);
222 break;
223 }
224
225 case ACTION_EXPORT:
226 {
227 WCHAR filename[MAX_PATH];
228
229 filename[0] = L'\0';
230 get_file_name(&s, filename);
231 if (!filename[0])
232 {
233 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified.");
234 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage);
235 exit(7);
236 }
237
238 if (s[0])
239 {
240 WCHAR reg_key_name[KEY_MAX_LEN];
241 get_file_name(&s, reg_key_name);
242 export_registry_key(filename, reg_key_name, REG_FORMAT_4);
243 }
244 else
245 {
246 export_registry_key(filename, NULL, REG_FORMAT_4);
247 }
248 break;
249 }
250
251 default:
252 fwprintf(stderr, L"%s: Unhandled action!\n", getAppName());
253 exit(8);
254 break;
255 }
256
257 return TRUE;
258 }
259
260 /**
261 * Process unknown switch.
262 *
263 * Params:
264 * chu - the switch character in upper-case.
265 * s - the command line string where s points to the switch character.
266 */
267 static void error_unknown_switch(WCHAR chu, LPWSTR s)
268 {
269 if (iswalpha(chu))
270 {
271 fwprintf(stderr, L"%s: Undefined switch /%c!\n", getAppName(), chu);
272 }
273 else
274 {
275 fwprintf(stderr, L"%s: Alphabetic character is expected after '%c' "
276 L"in switch specification\n", getAppName(), *(s - 1));
277 }
278 exit(1);
279 }
280
281 BOOL ProcessCmdLine(LPWSTR lpCmdLine)
282 {
283 BOOL silent = FALSE;
284 REGEDIT_ACTION action = ACTION_UNDEF;
285 LPWSTR s = lpCmdLine; /* command line pointer */
286 WCHAR ch = *s; /* current character */
287
288 while (ch && ((ch == L'-') || (ch == L'/')))
289 {
290 WCHAR chu;
291 WCHAR ch2;
292
293 s++;
294 ch = *s;
295 ch2 = *(s + 1);
296 chu = towupper(ch);
297 if (!ch2 || iswspace(ch2))
298 {
299 if (chu == L'S')
300 {
301 /* Silence dialogs */
302 silent = TRUE;
303 }
304 else if (chu == L'V')
305 {
306 /* Ignore this switch */
307 }
308 else
309 {
310 switch (chu)
311 {
312 case L'D':
313 action = ACTION_DELETE;
314 break;
315 case L'E':
316 action = ACTION_EXPORT;
317 break;
318 case L'?':
319 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage);
320 exit(3);
321 break;
322 default:
323 error_unknown_switch(chu, s);
324 break;
325 }
326 }
327 s++;
328 }
329 else
330 {
331 if (ch2 == L':')
332 {
333 switch (chu)
334 {
335 case L'L':
336 /* fall through */
337 case L'R':
338 s += 2;
339 while (*s && !iswspace(*s))
340 {
341 s++;
342 }
343 break;
344 default:
345 error_unknown_switch(chu, s);
346 break;
347 }
348 }
349 else
350 {
351 /* this is a file name, starting from '/' */
352 s--;
353 break;
354 }
355 }
356 /* skip spaces to the next parameter */
357 ch = *s;
358 while (ch && iswspace(ch))
359 {
360 s++;
361 ch = *s;
362 }
363 }
364
365 if (*s && action == ACTION_UNDEF)
366 action = ACTION_ADD;
367
368 if (action != ACTION_UNDEF)
369 return PerformRegAction(action, s, silent);
370 else
371 return FALSE;
372 }