622e7ca8d9465fba0fd4bcff61463ad293c1c9a2
[reactos.git] / base / applications / cmdutils / reg / export.c
1 /*
2 * Copyright 2017 Hugh McMaster
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <windows.h>
20 #include <stdlib.h>
21
22 #include <wine/unicode.h>
23 #include <wine/heap.h>
24
25 #include "reg.h"
26
27 static void write_file(HANDLE hFile, const WCHAR *str)
28 {
29 DWORD written;
30
31 WriteFile(hFile, str, lstrlenW(str) * sizeof(WCHAR), &written, NULL);
32 }
33
34 static WCHAR *escape_string(WCHAR *str, size_t str_len, size_t *line_len)
35 {
36 size_t i, escape_count, pos;
37 WCHAR *buf;
38
39 for (i = 0, escape_count = 0; i < str_len; i++)
40 {
41 WCHAR c = str[i];
42 if (c == '\r' || c == '\n' || c == '\\' || c == '"' || c == '\0')
43 escape_count++;
44 }
45
46 buf = heap_xalloc((str_len + escape_count + 1) * sizeof(WCHAR));
47
48 for (i = 0, pos = 0; i < str_len; i++, pos++)
49 {
50 WCHAR c = str[i];
51
52 switch (c)
53 {
54 case '\r':
55 buf[pos++] = '\\';
56 buf[pos] = 'r';
57 break;
58 case '\n':
59 buf[pos++] = '\\';
60 buf[pos] = 'n';
61 break;
62 case '\\':
63 buf[pos++] = '\\';
64 buf[pos] = '\\';
65 break;
66 case '"':
67 buf[pos++] = '\\';
68 buf[pos] = '"';
69 break;
70 case '\0':
71 buf[pos++] = '\\';
72 buf[pos] = '0';
73 break;
74 default:
75 buf[pos] = c;
76 }
77 }
78
79 buf[pos] = 0;
80 *line_len = pos;
81 return buf;
82 }
83
84 static size_t export_value_name(HANDLE hFile, WCHAR *name, size_t len)
85 {
86 static const WCHAR quoted_fmt[] = {'"','%','s','"','=',0};
87 static const WCHAR default_name[] = {'@','=',0};
88 size_t line_len;
89
90 if (name && *name)
91 {
92 WCHAR *str = escape_string(name, len, &line_len);
93 WCHAR *buf = heap_xalloc((line_len + 4) * sizeof(WCHAR));
94 line_len = sprintfW(buf, quoted_fmt, str);
95 write_file(hFile, buf);
96 heap_free(buf);
97 heap_free(str);
98 }
99 else
100 {
101 line_len = lstrlenW(default_name);
102 write_file(hFile, default_name);
103 }
104
105 return line_len;
106 }
107
108 static void export_string_data(WCHAR **buf, WCHAR *data, size_t size)
109 {
110 size_t len = 0, line_len;
111 WCHAR *str;
112 static const WCHAR fmt[] = {'"','%','s','"',0};
113
114 if (size)
115 len = size / sizeof(WCHAR) - 1;
116 str = escape_string(data, len, &line_len);
117 *buf = heap_xalloc((line_len + 3) * sizeof(WCHAR));
118 sprintfW(*buf, fmt, str);
119 heap_free(str);
120 }
121
122 static void export_dword_data(WCHAR **buf, DWORD *data)
123 {
124 static const WCHAR fmt[] = {'d','w','o','r','d',':','%','0','8','x',0};
125
126 *buf = heap_xalloc(15 * sizeof(WCHAR));
127 sprintfW(*buf, fmt, *data);
128 }
129
130 static size_t export_hex_data_type(HANDLE hFile, DWORD type)
131 {
132 static const WCHAR hex[] = {'h','e','x',':',0};
133 static const WCHAR hexp_fmt[] = {'h','e','x','(','%','x',')',':',0};
134 size_t line_len;
135
136 if (type == REG_BINARY)
137 {
138 line_len = lstrlenW(hex);
139 write_file(hFile, hex);
140 }
141 else
142 {
143 WCHAR *buf = heap_xalloc(15 * sizeof(WCHAR));
144 line_len = sprintfW(buf, hexp_fmt, type);
145 write_file(hFile, buf);
146 heap_free(buf);
147 }
148
149 return line_len;
150 }
151
152 #define MAX_HEX_CHARS 77
153
154 static void export_hex_data(HANDLE hFile, WCHAR **buf, DWORD type,
155 DWORD line_len, void *data, DWORD size)
156 {
157 static const WCHAR fmt[] = {'%','0','2','x',0};
158 static const WCHAR hex_concat[] = {'\\','\r','\n',' ',' ',0};
159 size_t num_commas, i, pos;
160
161 line_len += export_hex_data_type(hFile, type);
162
163 if (!size) return;
164
165 num_commas = size - 1;
166 *buf = heap_xalloc(size * 3 * sizeof(WCHAR));
167
168 for (i = 0, pos = 0; i < size; i++)
169 {
170 pos += sprintfW(*buf + pos, fmt, ((BYTE *)data)[i]);
171 if (i == num_commas) break;
172 (*buf)[pos++] = ',';
173 (*buf)[pos] = 0;
174 line_len += 3;
175
176 if (line_len >= MAX_HEX_CHARS)
177 {
178 write_file(hFile, *buf);
179 write_file(hFile, hex_concat);
180 line_len = 2;
181 pos = 0;
182 }
183 }
184 }
185
186 static void export_newline(HANDLE hFile)
187 {
188 static const WCHAR newline[] = {'\r','\n',0};
189
190 write_file(hFile, newline);
191 }
192
193 static void export_data(HANDLE hFile, WCHAR *value_name, DWORD value_len,
194 DWORD type, void *data, size_t size)
195 {
196 WCHAR *buf = NULL;
197 size_t line_len = export_value_name(hFile, value_name, value_len);
198
199 switch (type)
200 {
201 case REG_SZ:
202 export_string_data(&buf, data, size);
203 break;
204 case REG_DWORD:
205 if (size)
206 {
207 export_dword_data(&buf, data);
208 break;
209 }
210 /* fall through */
211 case REG_NONE:
212 case REG_EXPAND_SZ:
213 case REG_BINARY:
214 case REG_MULTI_SZ:
215 default:
216 export_hex_data(hFile, &buf, type, line_len, data, size);
217 break;
218 }
219
220 if (size || type == REG_SZ)
221 {
222 write_file(hFile, buf);
223 heap_free(buf);
224 }
225
226 export_newline(hFile);
227 }
228
229 static void export_key_name(HANDLE hFile, WCHAR *name)
230 {
231 static const WCHAR fmt[] = {'\r','\n','[','%','s',']','\r','\n',0};
232 WCHAR *buf;
233
234 buf = heap_xalloc((lstrlenW(name) + 7) * sizeof(WCHAR));
235 sprintfW(buf, fmt, name);
236 write_file(hFile, buf);
237 heap_free(buf);
238 }
239
240 static int export_registry_data(HANDLE hFile, HKEY key, WCHAR *path)
241 {
242 LONG rc;
243 DWORD max_value_len = 256, value_len;
244 DWORD max_data_bytes = 2048, data_size;
245 DWORD subkey_len;
246 DWORD i, type, path_len;
247 WCHAR *value_name, *subkey_name, *subkey_path;
248 BYTE *data;
249 HKEY subkey;
250
251 export_key_name(hFile, path);
252
253 value_name = heap_xalloc(max_value_len * sizeof(WCHAR));
254 data = heap_xalloc(max_data_bytes);
255
256 i = 0;
257 for (;;)
258 {
259 value_len = max_value_len;
260 data_size = max_data_bytes;
261 rc = RegEnumValueW(key, i, value_name, &value_len, NULL, &type, data, &data_size);
262
263 if (rc == ERROR_SUCCESS)
264 {
265 export_data(hFile, value_name, value_len, type, data, data_size);
266 i++;
267 }
268 else if (rc == ERROR_MORE_DATA)
269 {
270 if (data_size > max_data_bytes)
271 {
272 max_data_bytes = data_size;
273 data = heap_xrealloc(data, max_data_bytes);
274 }
275 else
276 {
277 max_value_len *= 2;
278 value_name = heap_xrealloc(value_name, max_value_len * sizeof(WCHAR));
279 }
280 }
281 else break;
282 }
283
284 heap_free(data);
285 heap_free(value_name);
286
287 subkey_name = heap_xalloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
288
289 path_len = lstrlenW(path);
290
291 i = 0;
292 for (;;)
293 {
294 subkey_len = MAX_SUBKEY_LEN;
295 rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
296 if (rc == ERROR_SUCCESS)
297 {
298 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
299 if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey))
300 {
301 export_registry_data(hFile, subkey, subkey_path);
302 RegCloseKey(subkey);
303 }
304 heap_free(subkey_path);
305 i++;
306 }
307 else break;
308 }
309
310 heap_free(subkey_name);
311 return 0;
312 }
313
314 static void export_file_header(HANDLE hFile)
315 {
316 static const WCHAR header[] = { 0xfeff,'W','i','n','d','o','w','s',' ',
317 'R','e','g','i','s','t','r','y',' ','E','d','i','t','o','r',' ',
318 'V','e','r','s','i','o','n',' ','5','.','0','0','\r','\n'};
319
320 write_file(hFile, header);
321 }
322
323 static HANDLE create_file(const WCHAR *filename, DWORD action)
324 {
325 return CreateFileW(filename, GENERIC_WRITE, 0, NULL, action, FILE_ATTRIBUTE_NORMAL, NULL);
326 }
327
328 static HANDLE get_file_handle(WCHAR *filename, BOOL overwrite_file)
329 {
330 HANDLE hFile = create_file(filename, overwrite_file ? CREATE_ALWAYS : CREATE_NEW);
331
332 if (hFile == INVALID_HANDLE_VALUE)
333 {
334 DWORD error = GetLastError();
335
336 if (error == ERROR_FILE_EXISTS)
337 {
338 if (!ask_confirm(STRING_OVERWRITE_FILE, filename))
339 {
340 output_message(STRING_CANCELLED);
341 exit(0);
342 }
343
344 hFile = create_file(filename, CREATE_ALWAYS);
345 }
346 else
347 {
348 WCHAR *str;
349
350 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
351 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (WCHAR *)&str, 0, NULL);
352 output_writeconsole(str, lstrlenW(str));
353 LocalFree(str);
354 exit(1);
355 }
356 }
357
358 return hFile;
359 }
360
361 static BOOL is_overwrite_switch(const WCHAR *s)
362 {
363 if (strlenW(s) > 2)
364 return FALSE;
365
366 if ((s[0] == '/' || s[0] == '-') && (s[1] == 'y' || s[1] == 'Y'))
367 return TRUE;
368
369 return FALSE;
370 }
371
372 int reg_export(int argc, WCHAR *argv[])
373 {
374 HKEY root, hkey;
375 WCHAR *path, *long_key;
376 BOOL overwrite_file = FALSE;
377 HANDLE hFile;
378 int ret;
379
380 if (argc == 3 || argc > 5)
381 goto error;
382
383 if (!parse_registry_key(argv[2], &root, &path, &long_key))
384 return 1;
385
386 if (argc == 5 && !(overwrite_file = is_overwrite_switch(argv[4])))
387 goto error;
388
389 if (RegOpenKeyExW(root, path, 0, KEY_READ, &hkey))
390 {
391 output_message(STRING_INVALID_KEY);
392 return 1;
393 }
394
395 hFile = get_file_handle(argv[3], overwrite_file);
396 export_file_header(hFile);
397 ret = export_registry_data(hFile, hkey, long_key);
398 export_newline(hFile);
399 CloseHandle(hFile);
400
401 RegCloseKey(hkey);
402
403 return ret;
404
405 error:
406 output_message(STRING_INVALID_SYNTAX);
407 output_message(STRING_FUNC_HELP, struprW(argv[1]));
408 return 1;
409 }