76534ae3345c4ef96705236e0a4f862336d2742d
[reactos.git] / reactos / 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 char *usage =
25 "Usage:\n"
26 " regedit filenames\n"
27 " regedit /E filename [regpath]\n"
28 " regedit /D regpath\n"
29 "\n"
30 "filenames - List of registry files names\n"
31 "filename - Registry file name\n"
32 "regpath - Name of the registry key\n"
33 "\n"
34 "When is called without any switches adds contents of the specified\n"
35 "registry files to the registry.\n"
36 "\n"
37 "Switches:\n"
38 " /E - Exports contents of the specified registry key to the specified\n"
39 " file. Exports the whole registry if no key is specified.\n"
40 " /D - Deletes specified registry key\n"
41 " /S - Silent execution, can be used with any other switch.\n"
42 " The only existing mode, exists for compatibility with Windows regedit.\n"
43 " /V - Advanced mode, can be used with any other switch.\n"
44 " Ignored, exists for compatibility with Windows regedit.\n"
45 " /L - Location of system.dat file. Can be used with any other switch.\n"
46 " Ignored. Exists for compatibility with Windows regedit.\n"
47 " /R - Location of user.dat file. Can be used with any other switch.\n"
48 " Ignored. Exists for compatibility with Windows regedit.\n"
49 " /? - Print this help. Any other switches are ignored.\n"
50 " /C - Create registry from. Not implemented.\n"
51 "\n"
52 "The switches are case-insensitive, can be prefixed either by '-' or '/'.\n"
53 "This program is command-line compatible with Microsoft Windows\n"
54 "regedit.\n";
55
56 typedef enum
57 {
58 ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
59 } REGEDIT_ACTION;
60
61
62 const CHAR *getAppName(void)
63 {
64 return "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(LPTSTR *command_line, LPTSTR file_name)
79 {
80 TCHAR *s = *command_line;
81 int 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] == _T('"'))
90 {
91 s++;
92 (*command_line)++;
93 while(s[0] != _T('"'))
94 {
95 if (!s[0])
96 {
97 fprintf(stderr, "%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] && !_istspace(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] == _T('\\'))
115 {
116 file_name[pos - 1] = _T('\0');
117 }
118 else
119 {
120 file_name[pos] = _T('\0');
121 }
122
123 if (s[0])
124 {
125 s++;
126 pos++;
127 }
128 while(s[0] && _istspace(s[0]))
129 {
130 s++;
131 pos++;
132 }
133 (*command_line) += pos;
134 }
135
136 BOOL PerformRegAction(REGEDIT_ACTION action, LPTSTR s, BOOL silent)
137 {
138 switch (action)
139 {
140 case ACTION_ADD:
141 {
142 TCHAR szTitle[512], szText[512];
143 TCHAR filename[MAX_PATH];
144 FILE *fp;
145
146 get_file_name(&s, filename);
147 if (!filename[0])
148 {
149 fprintf(stderr, "%s: No file name is specified\n", getAppName());
150 fprintf(stderr, usage);
151 exit(4);
152 }
153
154 LoadString(hInst, IDS_APP_TITLE, szTitle, COUNT_OF(szTitle));
155
156 while (filename[0])
157 {
158 /* Request import confirmation */
159 if (!silent)
160 {
161 LoadString(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 fp = _tfopen(filename, _T("r"));
168 if (fp != NULL)
169 {
170 import_registry_file(fp);
171
172 /* Show successful import */
173 if (!silent)
174 {
175 LoadString(hInst, IDS_IMPORT_OK, szText, COUNT_OF(szText));
176 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, szText, filename);
177 }
178 }
179 else
180 {
181 LPSTR p = GetMultiByteString(filename);
182 perror("");
183 fprintf(stderr, "%s: Can't open file \"%s\"\n", getAppName(), p);
184 HeapFree(GetProcessHeap(), 0, p);
185
186 /* Error opening the file */
187 if (!silent)
188 {
189 LoadString(hInst, IDS_IMPORT_ERROR, szText, COUNT_OF(szText));
190 InfoMessageBox(NULL, MB_OK | MB_ICONERROR, szTitle, szText, filename);
191 }
192 }
193
194 cont:
195 get_file_name(&s, filename);
196 }
197 break;
198 }
199
200 case ACTION_DELETE:
201 {
202 TCHAR reg_key_name[KEY_MAX_LEN];
203 get_file_name(&s, reg_key_name);
204 if (!reg_key_name[0])
205 {
206 fprintf(stderr, "%s: No registry key is specified for removal\n", getAppName());
207 fprintf(stderr, usage);
208 exit(6);
209 }
210 delete_registry_key(reg_key_name);
211 break;
212 }
213
214 case ACTION_EXPORT:
215 {
216 TCHAR filename[MAX_PATH];
217
218 filename[0] = _T('\0');
219 get_file_name(&s, filename);
220 if (!filename[0])
221 {
222 fprintf(stderr, "%s: No file name is specified\n", getAppName());
223 fprintf(stderr, usage);
224 exit(7);
225 }
226
227 if (s[0])
228 {
229 TCHAR reg_key_name[KEY_MAX_LEN];
230 get_file_name(&s, reg_key_name);
231 export_registry_key(filename, reg_key_name, REG_FORMAT_4);
232 }
233 else
234 {
235 export_registry_key(filename, NULL, REG_FORMAT_4);
236 }
237 break;
238 }
239
240 default:
241 fprintf(stderr, "%s: Unhandled action!\n", getAppName());
242 exit(8);
243 break;
244 }
245
246 return TRUE;
247 }
248
249 /**
250 * Process unknown switch.
251 *
252 * Params:
253 * chu - the switch character in upper-case.
254 * s - the command line string where s points to the switch character.
255 */
256 static void error_unknown_switch(WCHAR chu, LPWSTR s)
257 {
258 if (iswalpha(chu))
259 {
260 fprintf(stderr, "%s: Undefined switch /%c!\n", getAppName(), chu);
261 }
262 else
263 {
264 fprintf(stderr, "%s: Alphabetic character is expected after '%c' "
265 "in switch specification\n", getAppName(), *(s - 1));
266 }
267 exit(1);
268 }
269
270 BOOL ProcessCmdLine(LPTSTR lpCmdLine)
271 {
272 BOOL silent = FALSE;
273 REGEDIT_ACTION action = ACTION_UNDEF;
274 LPTSTR s = lpCmdLine; /* command line pointer */
275 TCHAR ch = *s; /* current character */
276
277 while (ch && ((ch == _T('-')) || (ch == _T('/'))))
278 {
279 TCHAR chu;
280 TCHAR ch2;
281
282 s++;
283 ch = *s;
284 ch2 = *(s + 1);
285 chu = _totupper(ch);
286 if (!ch2 || _istspace(ch2))
287 {
288 if (chu == _T('S'))
289 {
290 /* Silence dialogs */
291 silent = TRUE;
292 }
293 else if (chu == _T('V'))
294 {
295 /* Ignore this switch */
296 }
297 else
298 {
299 switch (chu)
300 {
301 case _T('D'):
302 action = ACTION_DELETE;
303 break;
304 case _T('E'):
305 action = ACTION_EXPORT;
306 break;
307 case _T('?'):
308 fprintf(stderr, usage);
309 exit(3);
310 break;
311 default:
312 error_unknown_switch(chu, s);
313 break;
314 }
315 }
316 s++;
317 }
318 else
319 {
320 if (ch2 == _T(':'))
321 {
322 switch (chu)
323 {
324 case _T('L'):
325 /* fall through */
326 case _T('R'):
327 s += 2;
328 while (*s && !_istspace(*s))
329 {
330 s++;
331 }
332 break;
333 default:
334 error_unknown_switch(chu, s);
335 break;
336 }
337 }
338 else
339 {
340 /* this is a file name, starting from '/' */
341 s--;
342 break;
343 }
344 }
345 /* skip spaces to the next parameter */
346 ch = *s;
347 while (ch && _istspace(ch))
348 {
349 s++;
350 ch = *s;
351 }
352 }
353
354 if (*s && action == ACTION_UNDEF)
355 action = ACTION_ADD;
356
357 if (action != ACTION_UNDEF)
358 return PerformRegAction(action, s, silent);
359 else
360 return FALSE;
361 }