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