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