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 <stdio.h> 21 #include <stdlib.h> 22 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 43 if (!c) break; 44 45 if (c == '\r' || c == '\n' || c == '\\' || c == '"') 46 escape_count++; 47 } 48 49 buf = heap_xalloc((str_len + escape_count + 1) * sizeof(WCHAR)); 50 51 for (i = 0, pos = 0; i < str_len; i++, pos++) 52 { 53 WCHAR c = str[i]; 54 55 if (!c) break; 56 57 switch (c) 58 { 59 case '\r': 60 buf[pos++] = '\\'; 61 buf[pos] = 'r'; 62 break; 63 case '\n': 64 buf[pos++] = '\\'; 65 buf[pos] = 'n'; 66 break; 67 case '\\': 68 buf[pos++] = '\\'; 69 buf[pos] = '\\'; 70 break; 71 case '"': 72 buf[pos++] = '\\'; 73 buf[pos] = '"'; 74 break; 75 default: 76 buf[pos] = c; 77 } 78 } 79 80 buf[pos] = 0; 81 *line_len = pos; 82 return buf; 83 } 84 85 static size_t export_value_name(HANDLE hFile, WCHAR *name, size_t len) 86 { 87 static const WCHAR quoted_fmt[] = {'"','%','s','"','=',0}; 88 static const WCHAR default_name[] = {'@','=',0}; 89 size_t line_len; 90 91 if (name && *name) 92 { 93 WCHAR *str = escape_string(name, len, &line_len); 94 WCHAR *buf = heap_xalloc((line_len + 4) * sizeof(WCHAR)); 95 line_len = swprintf(buf, quoted_fmt, str); 96 write_file(hFile, buf); 97 heap_free(buf); 98 heap_free(str); 99 } 100 else 101 { 102 line_len = lstrlenW(default_name); 103 write_file(hFile, default_name); 104 } 105 106 return line_len; 107 } 108 109 static void export_string_data(WCHAR **buf, WCHAR *data, size_t size) 110 { 111 size_t len = 0, line_len; 112 WCHAR *str; 113 static const WCHAR fmt[] = {'"','%','s','"',0}; 114 115 if (size) 116 len = size / sizeof(WCHAR) - 1; 117 str = escape_string(data, len, &line_len); 118 *buf = heap_xalloc((line_len + 3) * sizeof(WCHAR)); 119 swprintf(*buf, fmt, str); 120 heap_free(str); 121 } 122 123 static void export_dword_data(WCHAR **buf, DWORD *data) 124 { 125 static const WCHAR fmt[] = {'d','w','o','r','d',':','%','0','8','x',0}; 126 127 *buf = heap_xalloc(15 * sizeof(WCHAR)); 128 swprintf(*buf, fmt, *data); 129 } 130 131 static size_t export_hex_data_type(HANDLE hFile, DWORD type) 132 { 133 static const WCHAR hex[] = {'h','e','x',':',0}; 134 static const WCHAR hexp_fmt[] = {'h','e','x','(','%','x',')',':',0}; 135 size_t line_len; 136 137 if (type == REG_BINARY) 138 { 139 line_len = lstrlenW(hex); 140 write_file(hFile, hex); 141 } 142 else 143 { 144 WCHAR *buf = heap_xalloc(15 * sizeof(WCHAR)); 145 line_len = swprintf(buf, hexp_fmt, type); 146 write_file(hFile, buf); 147 heap_free(buf); 148 } 149 150 return line_len; 151 } 152 153 #define MAX_HEX_CHARS 77 154 155 static void export_hex_data(HANDLE hFile, WCHAR **buf, DWORD type, 156 DWORD line_len, void *data, DWORD size) 157 { 158 static const WCHAR fmt[] = {'%','0','2','x',0}; 159 static const WCHAR hex_concat[] = {'\\','\r','\n',' ',' ',0}; 160 size_t num_commas, i, pos; 161 162 line_len += export_hex_data_type(hFile, type); 163 164 if (!size) return; 165 166 num_commas = size - 1; 167 *buf = heap_xalloc(size * 3 * sizeof(WCHAR)); 168 169 for (i = 0, pos = 0; i < size; i++) 170 { 171 pos += swprintf(*buf + pos, fmt, ((BYTE *)data)[i]); 172 if (i == num_commas) break; 173 (*buf)[pos++] = ','; 174 (*buf)[pos] = 0; 175 line_len += 3; 176 177 if (line_len >= MAX_HEX_CHARS) 178 { 179 write_file(hFile, *buf); 180 write_file(hFile, hex_concat); 181 line_len = 2; 182 pos = 0; 183 } 184 } 185 } 186 187 static void export_newline(HANDLE hFile) 188 { 189 static const WCHAR newline[] = {'\r','\n',0}; 190 191 write_file(hFile, newline); 192 } 193 194 static void export_data(HANDLE hFile, WCHAR *value_name, DWORD value_len, 195 DWORD type, void *data, size_t size) 196 { 197 WCHAR *buf = NULL; 198 size_t line_len = export_value_name(hFile, value_name, value_len); 199 200 switch (type) 201 { 202 case REG_SZ: 203 export_string_data(&buf, data, size); 204 break; 205 case REG_DWORD: 206 if (size) 207 { 208 export_dword_data(&buf, data); 209 break; 210 } 211 /* fall through */ 212 case REG_NONE: 213 case REG_EXPAND_SZ: 214 case REG_BINARY: 215 case REG_MULTI_SZ: 216 default: 217 export_hex_data(hFile, &buf, type, line_len, data, size); 218 break; 219 } 220 221 if (size || type == REG_SZ) 222 { 223 write_file(hFile, buf); 224 heap_free(buf); 225 } 226 227 export_newline(hFile); 228 } 229 230 static void export_key_name(HANDLE hFile, WCHAR *name) 231 { 232 static const WCHAR fmt[] = {'\r','\n','[','%','s',']','\r','\n',0}; 233 WCHAR *buf; 234 235 buf = heap_xalloc((lstrlenW(name) + 7) * sizeof(WCHAR)); 236 swprintf(buf, fmt, name); 237 write_file(hFile, buf); 238 heap_free(buf); 239 } 240 241 static int export_registry_data(HANDLE hFile, HKEY key, WCHAR *path) 242 { 243 LONG rc; 244 DWORD max_value_len = 256, value_len; 245 DWORD max_data_bytes = 2048, data_size; 246 DWORD subkey_len; 247 DWORD i, type, path_len; 248 WCHAR *value_name, *subkey_name, *subkey_path; 249 BYTE *data; 250 HKEY subkey; 251 252 export_key_name(hFile, path); 253 254 value_name = heap_xalloc(max_value_len * sizeof(WCHAR)); 255 data = heap_xalloc(max_data_bytes); 256 257 i = 0; 258 for (;;) 259 { 260 value_len = max_value_len; 261 data_size = max_data_bytes; 262 rc = RegEnumValueW(key, i, value_name, &value_len, NULL, &type, data, &data_size); 263 264 if (rc == ERROR_SUCCESS) 265 { 266 export_data(hFile, value_name, value_len, type, data, data_size); 267 i++; 268 } 269 else if (rc == ERROR_MORE_DATA) 270 { 271 if (data_size > max_data_bytes) 272 { 273 max_data_bytes = data_size; 274 data = heap_xrealloc(data, max_data_bytes); 275 } 276 else 277 { 278 max_value_len *= 2; 279 value_name = heap_xrealloc(value_name, max_value_len * sizeof(WCHAR)); 280 } 281 } 282 else break; 283 } 284 285 heap_free(data); 286 heap_free(value_name); 287 288 subkey_name = heap_xalloc(MAX_SUBKEY_LEN * sizeof(WCHAR)); 289 290 path_len = lstrlenW(path); 291 292 i = 0; 293 for (;;) 294 { 295 subkey_len = MAX_SUBKEY_LEN; 296 rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL); 297 if (rc == ERROR_SUCCESS) 298 { 299 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len); 300 if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey)) 301 { 302 export_registry_data(hFile, subkey, subkey_path); 303 RegCloseKey(subkey); 304 } 305 heap_free(subkey_path); 306 i++; 307 } 308 else break; 309 } 310 311 heap_free(subkey_name); 312 return 0; 313 } 314 315 static void export_file_header(HANDLE hFile) 316 { 317 static const WCHAR header[] = { 0xfeff,'W','i','n','d','o','w','s',' ', 318 'R','e','g','i','s','t','r','y',' ','E','d','i','t','o','r',' ', 319 'V','e','r','s','i','o','n',' ','5','.','0','0','\r','\n',0}; 320 321 write_file(hFile, header); 322 } 323 324 static HANDLE create_file(const WCHAR *filename, DWORD action) 325 { 326 return CreateFileW(filename, GENERIC_WRITE, 0, NULL, action, FILE_ATTRIBUTE_NORMAL, NULL); 327 } 328 329 static HANDLE get_file_handle(WCHAR *filename, BOOL overwrite_file) 330 { 331 HANDLE hFile = create_file(filename, overwrite_file ? CREATE_ALWAYS : CREATE_NEW); 332 333 if (hFile == INVALID_HANDLE_VALUE) 334 { 335 DWORD error = GetLastError(); 336 337 if (error == ERROR_FILE_EXISTS) 338 { 339 if (!ask_confirm(STRING_OVERWRITE_FILE, filename)) 340 { 341 output_message(STRING_CANCELLED); 342 exit(0); 343 } 344 345 hFile = create_file(filename, CREATE_ALWAYS); 346 } 347 else 348 { 349 WCHAR *str; 350 351 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 352 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (WCHAR *)&str, 0, NULL); 353 output_writeconsole(str, lstrlenW(str)); 354 LocalFree(str); 355 exit(1); 356 } 357 } 358 359 return hFile; 360 } 361 362 static BOOL is_overwrite_switch(const WCHAR *s) 363 { 364 if (lstrlenW(s) > 2) 365 return FALSE; 366 367 if ((s[0] == '/' || s[0] == '-') && (s[1] == 'y' || s[1] == 'Y')) 368 return TRUE; 369 370 return FALSE; 371 } 372 373 int reg_export(int argc, WCHAR *argv[]) 374 { 375 HKEY root, hkey; 376 WCHAR *path, *long_key; 377 BOOL overwrite_file = FALSE; 378 HANDLE hFile; 379 int ret; 380 381 if (argc == 3 || argc > 5) 382 goto error; 383 384 if (!parse_registry_key(argv[2], &root, &path, &long_key)) 385 return 1; 386 387 if (argc == 5 && !(overwrite_file = is_overwrite_switch(argv[4]))) 388 goto error; 389 390 if (RegOpenKeyExW(root, path, 0, KEY_READ, &hkey)) 391 { 392 output_message(STRING_INVALID_KEY); 393 return 1; 394 } 395 396 hFile = get_file_handle(argv[3], overwrite_file); 397 export_file_header(hFile); 398 ret = export_registry_data(hFile, hkey, long_key); 399 export_newline(hFile); 400 CloseHandle(hFile); 401 402 RegCloseKey(hkey); 403 404 return ret; 405 406 error: 407 output_message(STRING_INVALID_SYNTAX); 408 output_message(STRING_FUNC_HELP, _wcsupr(argv[1])); 409 return 1; 410 } 411