regedit: Use W version of TreeView_GetItem.
[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                 import_registry_file(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             } else
190             {
191                 WCHAR* reg_key_nameW = GetWideString(reg_key_name);
192                 delete_registry_key(reg_key_nameW);
193                 HeapFree(GetProcessHeap(), 0, reg_key_nameW);
194             }
195             break;
196         }
197     case ACTION_EXPORT: {
198             CHAR filename[MAX_PATH];
199             WCHAR* filenameW;
200
201             filename[0] = '\0';
202             get_file_name(&s, filename);
203             if (!filename[0]) {
204                 fprintf(stderr,"%s: No file name was specified\n", getAppName());
205                 fprintf(stderr,usage);
206                 exit(1);
207             }
208
209             filenameW = GetWideString(filename);
210             if (s[0]) {
211                 CHAR reg_key_name[KEY_MAX_LEN];
212                 WCHAR* reg_key_nameW;
213
214                 get_file_name(&s, reg_key_name);
215                 reg_key_nameW = GetWideString(reg_key_name);
216                 export_registry_key(filenameW, reg_key_nameW, REG_FORMAT_4);
217                 HeapFree(GetProcessHeap(), 0, reg_key_nameW);
218             } else {
219                 export_registry_key(filenameW, NULL, REG_FORMAT_4);
220             }
221             HeapFree(GetProcessHeap(), 0, filenameW);
222             break;
223         }
224     default:
225         fprintf(stderr,"%s: Unhandled action!\n", getAppName());
226         exit(1);
227         break;
228     }
229     return TRUE;
230 }
231
232 /**
233  * Process unknown switch.
234  *
235  * Params:
236  *   chu - the switch character in upper-case.
237  *   s - the command line string where s points to the switch character.
238  */
239 static void error_unknown_switch(char chu, char *s)
240 {
241     if (isalpha(chu)) {
242         fprintf(stderr,"%s: Undefined switch /%c!\n", getAppName(), chu);
243     } else {
244         fprintf(stderr,"%s: Alphabetic character is expected after '%c' "
245                 "in switch specification\n", getAppName(), *(s - 1));
246     }
247     exit(1);
248 }
249
250 BOOL ProcessCmdLine(LPSTR lpCmdLine)
251 {
252     REGEDIT_ACTION action = ACTION_UNDEF;
253     LPSTR s = lpCmdLine;        /* command line pointer */
254     CHAR ch = *s;               /* current character */
255
256     while (ch && ((ch == '-') || (ch == '/'))) {
257         char chu;
258         char ch2;
259
260         s++;
261         ch = *s;
262         if (!ch || isspace(ch))
263         {
264             /* '-' is a file name. It indicates we should use stdin */
265             s--;
266             break;
267         }
268         ch2 = *(s+1);
269         chu = toupper(ch);
270         if (!ch2 || isspace(ch2)) {
271             if (chu == 'S' || chu == 'V') {
272                 /* ignore these switches */
273             } else {
274                 switch (chu) {
275                 case 'D':
276                     action = ACTION_DELETE;
277                     break;
278                 case 'E':
279                     action = ACTION_EXPORT;
280                     break;
281                 case '?':
282                     fprintf(stderr,usage);
283                     exit(0);
284                     break;
285                 default:
286                     error_unknown_switch(chu, s);
287                     break;
288                 }
289             }
290             s++;
291         } else {
292             if (ch2 == ':') {
293                 switch (chu) {
294                 case 'L':
295                     /* fall through */
296                 case 'R':
297                     s += 2;
298                     while (*s && !isspace(*s)) {
299                         s++;
300                     }
301                     break;
302                 default:
303                     error_unknown_switch(chu, s);
304                     break;
305                 }
306             } else {
307                 /* this is a file name, starting from '/' */
308                 s--;
309                 break;
310             }
311         }
312         /* skip spaces to the next parameter */
313         ch = *s;
314         while (ch && isspace(ch)) {
315             s++;
316             ch = *s;
317         }
318     }
319
320     if (*s && action == ACTION_UNDEF)
321         action = ACTION_ADD;
322
323     if (action == ACTION_UNDEF)
324         return FALSE;
325
326     return PerformRegAction(action, s);
327 }