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