regedit: Convert import delete handling to unicode.
[wine] / programs / regedit / regedit.c
1 /*
2  * Windows regedit.exe registry editor implementation.
3  *
4  * Copyright 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 St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <windows.h>
24 #include "regproc.h"
25
26 static const char *usage =
27     "Usage:\n"
28     "    regedit filename\n"
29     "    regedit /E filename [regpath]\n"
30     "    regedit /D regpath\n"
31     "\n"
32     "filename - registry file name\n"
33     "regpath - name of the registry key\n"
34     "\n"
35     "When called without any switches, adds the content of the specified\n"
36     "file to the registry\n"
37     "\n"
38     "Switches:\n"
39     "    /E - exports contents of the specified registry key to the specified\n"
40     "   file. Exports the whole registry if no key is specified.\n"
41     "    /D - deletes specified registry key\n"
42     "    /S - silent execution, can be used with any other switch.\n"
43     "   Default. The only existing mode, exists for compatibility with Windows regedit.\n"
44     "    /V - advanced mode, can be used with any other switch.\n"
45     "   Ignored, exists for compatibility with Windows regedit.\n"
46     "    /L - location of system.dat file. Can be used with any other switch.\n"
47     "   Ignored. Exists for compatibility with Windows regedit.\n"
48     "    /R - location of user.dat file. Can be used with any other switch.\n"
49     "   Ignored. Exists for compatibility with Windows regedit.\n"
50     "    /? - print this help. Any other switches are ignored.\n"
51     "    /C - create registry from file. Not implemented.\n"
52     "\n"
53     "The switches are case-insensitive, can be prefixed either by '-' or '/'.\n"
54     "This program is command-line compatible with Microsoft Windows\n"
55     "regedit.\n";
56
57 typedef enum {
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 static void get_file_name(CHAR **command_line, CHAR *file_name)
79 {
80     CHAR *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         return;
86     }
87
88     if (s[0] == '"') {
89         s++;
90         (*command_line)++;
91         while(s[0] != '"') {
92             if (!s[0]) {
93                 fprintf(stderr,"%s: Unexpected end of file name!\n",
94                         getAppName());
95                 exit(1);
96             }
97             s++;
98             pos++;
99         }
100     } else {
101         while(s[0] && !isspace(s[0])) {
102             s++;
103             pos++;
104         }
105     }
106     memcpy(file_name, *command_line, pos * sizeof((*command_line)[0]));
107     /* remove the last backslash */
108     if (file_name[pos - 1] == '\\') {
109         file_name[pos - 1] = '\0';
110     } else {
111         file_name[pos] = '\0';
112     }
113
114     if (s[0]) {
115         s++;
116         pos++;
117     }
118     while(s[0] && isspace(s[0])) {
119         s++;
120         pos++;
121     }
122     (*command_line) += pos;
123 }
124
125 static BOOL PerformRegAction(REGEDIT_ACTION action, LPSTR s)
126 {
127     switch (action) {
128     case ACTION_ADD: {
129             CHAR filename[MAX_PATH];
130             FILE *reg_file;
131
132             get_file_name(&s, filename);
133             if (!filename[0]) {
134                 fprintf(stderr,"%s: No file name was specified\n", getAppName());
135                 fprintf(stderr,usage);
136                 exit(1);
137             }
138
139             while(filename[0]) {
140                 char* realname = NULL;
141
142                 if (strcmp(filename, "-") == 0)
143                 {
144                     reg_file=stdin;
145                 }
146                 else
147                 {
148                     int size;
149
150                     size=SearchPath(NULL, filename, NULL,0, NULL, NULL);
151                     if (size>0)
152                     {
153                         realname=HeapAlloc(GetProcessHeap(), 0, size);
154                         size=SearchPath(NULL, filename, NULL, size, realname, NULL);
155                     }
156                     if (size==0)
157                     {
158                         fprintf(stderr, "%s: File not found \"%s\" (%d)\n",
159                                 getAppName(), filename, GetLastError());
160                         exit(1);
161                     }
162                     reg_file = fopen(realname, "r");
163                     if (reg_file==NULL)
164                     {
165                         perror("");
166                         fprintf(stderr,"%s: Can't open file \"%s\"\n", getAppName(), filename);
167                         exit(1);
168                     }
169                 }
170                 processRegLines(reg_file);
171                 if (realname)
172                 {
173                     HeapFree(GetProcessHeap(),0,realname);
174                     fclose(reg_file);
175                 }
176                 get_file_name(&s, filename);
177             }
178             break;
179         }
180     case ACTION_DELETE: {
181             CHAR reg_key_name[KEY_MAX_LEN];
182
183             get_file_name(&s, reg_key_name);
184             if (!reg_key_name[0]) {
185                 fprintf(stderr,"%s: No registry key was specified for removal\n",
186                         getAppName());
187                 fprintf(stderr,usage);
188                 exit(1);
189             }
190             delete_registry_key(reg_key_name);
191             break;
192         }
193     case ACTION_EXPORT: {
194             CHAR filename[MAX_PATH];
195
196             filename[0] = '\0';
197             get_file_name(&s, filename);
198             if (!filename[0]) {
199                 fprintf(stderr,"%s: No file name was specified\n", getAppName());
200                 fprintf(stderr,usage);
201                 exit(1);
202             }
203
204             if (s[0]) {
205                 CHAR reg_key_name[KEY_MAX_LEN];
206
207                 get_file_name(&s, reg_key_name);
208                 export_registry_key(filename, reg_key_name);
209             } else {
210                 export_registry_key(filename, NULL);
211             }
212             break;
213         }
214     default:
215         fprintf(stderr,"%s: Unhandled action!\n", getAppName());
216         exit(1);
217         break;
218     }
219     return TRUE;
220 }
221
222 /**
223  * Process unknown switch.
224  *
225  * Params:
226  *   chu - the switch character in upper-case.
227  *   s - the command line string where s points to the switch character.
228  */
229 static void error_unknown_switch(char chu, char *s)
230 {
231     if (isalpha(chu)) {
232         fprintf(stderr,"%s: Undefined switch /%c!\n", getAppName(), chu);
233     } else {
234         fprintf(stderr,"%s: Alphabetic character is expected after '%c' "
235                 "in switch specification\n", getAppName(), *(s - 1));
236     }
237     exit(1);
238 }
239
240 BOOL ProcessCmdLine(LPSTR lpCmdLine)
241 {
242     REGEDIT_ACTION action = ACTION_UNDEF;
243     LPSTR s = lpCmdLine;        /* command line pointer */
244     CHAR ch = *s;               /* current character */
245
246     while (ch && ((ch == '-') || (ch == '/'))) {
247         char chu;
248         char ch2;
249
250         s++;
251         ch = *s;
252         if (!ch || isspace(ch))
253         {
254             /* '-' is a file name. It indicates we should use stdin */
255             s--;
256             break;
257         }
258         ch2 = *(s+1);
259         chu = toupper(ch);
260         if (!ch2 || isspace(ch2)) {
261             if (chu == 'S' || chu == 'V') {
262                 /* ignore these switches */
263             } else {
264                 switch (chu) {
265                 case 'D':
266                     action = ACTION_DELETE;
267                     break;
268                 case 'E':
269                     action = ACTION_EXPORT;
270                     break;
271                 case '?':
272                     fprintf(stderr,usage);
273                     exit(0);
274                     break;
275                 default:
276                     error_unknown_switch(chu, s);
277                     break;
278                 }
279             }
280             s++;
281         } else {
282             if (ch2 == ':') {
283                 switch (chu) {
284                 case 'L':
285                     /* fall through */
286                 case 'R':
287                     s += 2;
288                     while (*s && !isspace(*s)) {
289                         s++;
290                     }
291                     break;
292                 default:
293                     error_unknown_switch(chu, s);
294                     break;
295                 }
296             } else {
297                 /* this is a file name, starting from '/' */
298                 s--;
299                 break;
300             }
301         }
302         /* skip spaces to the next parameter */
303         ch = *s;
304         while (ch && isspace(ch)) {
305             s++;
306             ch = *s;
307         }
308     }
309
310     if (*s && action == ACTION_UNDEF)
311         action = ACTION_ADD;
312
313     if (action == ACTION_UNDEF)
314         return FALSE;
315
316     return PerformRegAction(action, s);
317 }