Sync with trunk rev.61910 to get latest improvements and bugfixes.
[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 szTitle[512], 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, NULL, usage);
150 exit(4);
151 }
152
153 LoadStringW(hInst, IDS_APP_TITLE, szTitle, COUNT_OF(szTitle));
154
155 while (filename[0])
156 {
157 /* Request import confirmation */
158 if (!silent)
159 {
160 int choice;
161
162 LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, COUNT_OF(szText));
163
164 choice = InfoMessageBox(NULL, MB_YESNOCANCEL | MB_ICONWARNING, szTitle, szText, filename);
165
166 switch (choice)
167 {
168 case IDNO:
169 goto cont;
170 case IDCANCEL:
171 /* The cancel case is useful if the user is importing more than one registry file
172 at a time, and wants to back out anytime during the import process. This way, the
173 user doesn't have to resort to ending the regedit process abruptly just to cancel
174 the operation. */
175 return TRUE;
176 default:
177 break;
178 }
179 }
180
181 /* Open the file */
182 fp = _wfopen(filename, L"r");
183
184 /* Import it */
185 if (fp == NULL || !import_registry_file(fp))
186 {
187 /* Error opening the file */
188 if (!silent)
189 {
190 LoadStringW(hInst, IDS_IMPORT_ERROR, szText, COUNT_OF(szText));
191 InfoMessageBox(NULL, MB_OK | MB_ICONERROR, szTitle, szText, filename);
192 }
193 }
194 else
195 {
196 /* Show successful import */
197 if (!silent)
198 {
199 LoadStringW(hInst, IDS_IMPORT_OK, szText, COUNT_OF(szText));
200 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, szText, filename);
201 }
202 }
203
204 /* Close the file */
205 if (fp) fclose(fp);
206
207 cont:
208 get_file_name(&s, filename);
209 }
210 break;
211 }
212
213 case ACTION_DELETE:
214 {
215 WCHAR reg_key_name[KEY_MAX_LEN];
216 get_file_name(&s, reg_key_name);
217 if (!reg_key_name[0])
218 {
219 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No registry key is specified for removal.");
220 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
221 exit(6);
222 }
223 delete_registry_key(reg_key_name);
224 break;
225 }
226
227 case ACTION_EXPORT:
228 {
229 WCHAR filename[MAX_PATH];
230
231 filename[0] = L'\0';
232 get_file_name(&s, filename);
233 if (!filename[0])
234 {
235 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified.");
236 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
237 exit(7);
238 }
239
240 if (s[0])
241 {
242 WCHAR reg_key_name[KEY_MAX_LEN];
243 get_file_name(&s, reg_key_name);
244 export_registry_key(filename, reg_key_name, REG_FORMAT_4);
245 }
246 else
247 {
248 export_registry_key(filename, NULL, REG_FORMAT_4);
249 }
250 break;
251 }
252
253 default:
254 fwprintf(stderr, L"%s: Unhandled action!\n", getAppName());
255 exit(8);
256 break;
257 }
258
259 return TRUE;
260 }
261
262 /**
263 * Process unknown switch.
264 *
265 * Params:
266 * chu - the switch character in upper-case.
267 * s - the command line string where s points to the switch character.
268 */
269 static void error_unknown_switch(WCHAR chu, LPWSTR s)
270 {
271 if (iswalpha(chu))
272 {
273 fwprintf(stderr, L"%s: Undefined switch /%c!\n", getAppName(), chu);
274 }
275 else
276 {
277 fwprintf(stderr, L"%s: Alphabetic character is expected after '%c' "
278 L"in switch specification\n", getAppName(), *(s - 1));
279 }
280 exit(1);
281 }
282
283 BOOL ProcessCmdLine(LPWSTR lpCmdLine)
284 {
285 BOOL silent = FALSE;
286 REGEDIT_ACTION action = ACTION_UNDEF;
287 LPWSTR s = lpCmdLine; /* command line pointer */
288 WCHAR ch = *s; /* current character */
289
290 while (ch && ((ch == L'-') || (ch == L'/')))
291 {
292 WCHAR chu;
293 WCHAR ch2;
294
295 s++;
296 ch = *s;
297 ch2 = *(s + 1);
298 chu = towupper(ch);
299 if (!ch2 || iswspace(ch2))
300 {
301 if (chu == L'S')
302 {
303 /* Silence dialogs */
304 silent = TRUE;
305 }
306 else if (chu == L'V')
307 {
308 /* Ignore this switch */
309 }
310 else
311 {
312 switch (chu)
313 {
314 case L'D':
315 action = ACTION_DELETE;
316 break;
317 case L'E':
318 action = ACTION_EXPORT;
319 break;
320 case L'?':
321 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
322 exit(3);
323 break;
324 default:
325 error_unknown_switch(chu, s);
326 break;
327 }
328 }
329 s++;
330 }
331 else
332 {
333 if (ch2 == L':')
334 {
335 switch (chu)
336 {
337 case L'L':
338 /* fall through */
339 case L'R':
340 s += 2;
341 while (*s && !iswspace(*s))
342 {
343 s++;
344 }
345 break;
346 default:
347 error_unknown_switch(chu, s);
348 break;
349 }
350 }
351 else
352 {
353 /* this is a file name, starting from '/' */
354 s--;
355 break;
356 }
357 }
358 /* skip spaces to the next parameter */
359 ch = *s;
360 while (ch && iswspace(ch))
361 {
362 s++;
363 ch = *s;
364 }
365 }
366
367 if (*s && action == ACTION_UNDEF)
368 action = ACTION_ADD;
369
370 if (action != ACTION_UNDEF)
371 return PerformRegAction(action, s, silent);
372 else
373 return FALSE;
374 }