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