cmd.exe: Fix redirect ordering on a command line.
[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                 int size;
142                 size=SearchPath(NULL,filename,NULL,0,NULL,NULL);
143                 if (size>0)
144                 {
145                     realname=HeapAlloc(GetProcessHeap(),0,size);
146                     size=SearchPath(NULL,filename,NULL,size,realname,NULL);
147                 }
148                 if (size==0)
149                 {
150                     fprintf(stderr,"%s: File not found \"%s\" (%d)\n",
151                             getAppName(),filename,GetLastError());
152                     exit(1);
153                 }
154                 reg_file = fopen(realname, "r");
155                 if (reg_file==NULL)
156                 {
157                     perror("");
158                     fprintf(stderr,"%s: Can't open file \"%s\"\n", getAppName(), filename);
159                     exit(1);
160                 }
161                 processRegLines(reg_file);
162                 if (realname)
163                 {
164                     HeapFree(GetProcessHeap(),0,realname);
165                     fclose(reg_file);
166                 }
167                 get_file_name(&s, filename);
168             }
169             break;
170         }
171     case ACTION_DELETE: {
172             CHAR reg_key_name[KEY_MAX_LEN];
173
174             get_file_name(&s, reg_key_name);
175             if (!reg_key_name[0]) {
176                 fprintf(stderr,"%s: No registry key was specified for removal\n",
177                         getAppName());
178                 fprintf(stderr,usage);
179                 exit(1);
180             }
181             delete_registry_key(reg_key_name);
182             break;
183         }
184     case ACTION_EXPORT: {
185             CHAR filename[MAX_PATH];
186
187             filename[0] = '\0';
188             get_file_name(&s, filename);
189             if (!filename[0]) {
190                 fprintf(stderr,"%s: No file name was specified\n", getAppName());
191                 fprintf(stderr,usage);
192                 exit(1);
193             }
194
195             if (s[0]) {
196                 CHAR reg_key_name[KEY_MAX_LEN];
197
198                 get_file_name(&s, reg_key_name);
199                 export_registry_key(filename, reg_key_name);
200             } else {
201                 export_registry_key(filename, NULL);
202             }
203             break;
204         }
205     default:
206         fprintf(stderr,"%s: Unhandled action!\n", getAppName());
207         exit(1);
208         break;
209     }
210     return TRUE;
211 }
212
213 /**
214  * Process unknown switch.
215  *
216  * Params:
217  *   chu - the switch character in upper-case.
218  *   s - the command line string where s points to the switch character.
219  */
220 static void error_unknown_switch(char chu, char *s)
221 {
222     if (isalpha(chu)) {
223         fprintf(stderr,"%s: Undefined switch /%c!\n", getAppName(), chu);
224     } else {
225         fprintf(stderr,"%s: Alphabetic character is expected after '%c' "
226                 "in switch specification\n", getAppName(), *(s - 1));
227     }
228     exit(1);
229 }
230
231 BOOL ProcessCmdLine(LPSTR lpCmdLine)
232 {
233     REGEDIT_ACTION action = ACTION_UNDEF;
234     LPSTR s = lpCmdLine;        /* command line pointer */
235     CHAR ch = *s;               /* current character */
236
237     while (ch && ((ch == '-') || (ch == '/'))) {
238         char chu;
239         char ch2;
240
241         s++;
242         ch = *s;
243         ch2 = *(s+1);
244         chu = toupper(ch);
245         if (!ch2 || isspace(ch2)) {
246             if (chu == 'S' || chu == 'V') {
247                 /* ignore these switches */
248             } else {
249                 switch (chu) {
250                 case 'D':
251                     action = ACTION_DELETE;
252                     break;
253                 case 'E':
254                     action = ACTION_EXPORT;
255                     break;
256                 case '?':
257                     fprintf(stderr,usage);
258                     exit(0);
259                     break;
260                 default:
261                     error_unknown_switch(chu, s);
262                     break;
263                 }
264             }
265             s++;
266         } else {
267             if (ch2 == ':') {
268                 switch (chu) {
269                 case 'L':
270                     /* fall through */
271                 case 'R':
272                     s += 2;
273                     while (*s && !isspace(*s)) {
274                         s++;
275                     }
276                     break;
277                 default:
278                     error_unknown_switch(chu, s);
279                     break;
280                 }
281             } else {
282                 /* this is a file name, starting from '/' */
283                 s--;
284                 break;
285             }
286         }
287         /* skip spaces to the next parameter */
288         ch = *s;
289         while (ch && isspace(ch)) {
290             s++;
291             ch = *s;
292         }
293     }
294
295     if (*s && action == ACTION_UNDEF)
296         action = ACTION_ADD;
297
298     if (action == ACTION_UNDEF)
299         return FALSE;
300
301     return PerformRegAction(action, s);
302 }