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