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