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