1 /* 2 * Copyright 2008 Andrew Riedi 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 <stdarg.h> 20 #include <windef.h> 21 #include <winbase.h> 22 #include <winuser.h> 23 #include <winreg.h> 24 #include <wincon.h> 25 #include <shlwapi.h> 26 #include <wine/unicode.h> 27 #include <wine/debug.h> 28 #include "reg.h" 29 30 WINE_DEFAULT_DEBUG_CHANNEL(reg); 31 32 static const WCHAR short_hklm[] = {'H','K','L','M',0}; 33 static const WCHAR short_hkcu[] = {'H','K','C','U',0}; 34 static const WCHAR short_hkcr[] = {'H','K','C','R',0}; 35 static const WCHAR short_hku[] = {'H','K','U',0}; 36 static const WCHAR short_hkcc[] = {'H','K','C','C',0}; 37 static const WCHAR long_hklm[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',0}; 38 static const WCHAR long_hkcu[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0}; 39 static const WCHAR long_hkcr[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0}; 40 static const WCHAR long_hku[] = {'H','K','E','Y','_','U','S','E','R','S',0}; 41 static const WCHAR long_hkcc[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0}; 42 43 static const struct 44 { 45 HKEY key; 46 const WCHAR *short_name; 47 const WCHAR *long_name; 48 } 49 root_rels[] = 50 { 51 {HKEY_LOCAL_MACHINE, short_hklm, long_hklm}, 52 {HKEY_CURRENT_USER, short_hkcu, long_hkcu}, 53 {HKEY_CLASSES_ROOT, short_hkcr, long_hkcr}, 54 {HKEY_USERS, short_hku, long_hku}, 55 {HKEY_CURRENT_CONFIG, short_hkcc, long_hkcc}, 56 }; 57 58 static const WCHAR type_none[] = {'R','E','G','_','N','O','N','E',0}; 59 static const WCHAR type_sz[] = {'R','E','G','_','S','Z',0}; 60 static const WCHAR type_expand_sz[] = {'R','E','G','_','E','X','P','A','N','D','_','S','Z',0}; 61 static const WCHAR type_binary[] = {'R','E','G','_','B','I','N','A','R','Y',0}; 62 static const WCHAR type_dword[] = {'R','E','G','_','D','W','O','R','D',0}; 63 static const WCHAR type_dword_le[] = {'R','E','G','_','D','W','O','R','D','_','L','I','T','T','L','E','_','E','N','D','I','A','N',0}; 64 static const WCHAR type_dword_be[] = {'R','E','G','_','D','W','O','R','D','_','B','I','G','_','E','N','D','I','A','N',0}; 65 static const WCHAR type_multi_sz[] = {'R','E','G','_','M','U','L','T','I','_','S','Z',0}; 66 67 static const struct 68 { 69 DWORD type; 70 const WCHAR *name; 71 } 72 type_rels[] = 73 { 74 {REG_NONE, type_none}, 75 {REG_SZ, type_sz}, 76 {REG_EXPAND_SZ, type_expand_sz}, 77 {REG_BINARY, type_binary}, 78 {REG_DWORD, type_dword}, 79 {REG_DWORD_LITTLE_ENDIAN, type_dword_le}, 80 {REG_DWORD_BIG_ENDIAN, type_dword_be}, 81 {REG_MULTI_SZ, type_multi_sz}, 82 }; 83 84 static const WCHAR newlineW[] = {'\n',0}; 85 86 void *heap_xalloc(size_t size) 87 { 88 void *buf = HeapAlloc(GetProcessHeap(), 0, size); 89 if (!buf) 90 { 91 ERR("Out of memory!\n"); 92 exit(1); 93 } 94 return buf; 95 } 96 97 void *heap_xrealloc(void *buf, size_t size) 98 { 99 void *new_buf; 100 101 if (buf) 102 new_buf = HeapReAlloc(GetProcessHeap(), 0, buf, size); 103 else 104 new_buf = HeapAlloc(GetProcessHeap(), 0, size); 105 106 if (!new_buf) 107 { 108 ERR("Out of memory!\n"); 109 exit(1); 110 } 111 112 return new_buf; 113 } 114 115 BOOL heap_free(void *buf) 116 { 117 return HeapFree(GetProcessHeap(), 0, buf); 118 } 119 120 void output_writeconsole(const WCHAR *str, DWORD wlen) 121 { 122 DWORD count, ret; 123 124 ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL); 125 if (!ret) 126 { 127 DWORD len; 128 char *msgA; 129 130 /* On Windows WriteConsoleW() fails if the output is redirected. So fall 131 * back to WriteFile(), assuming the console encoding is still the right 132 * one in that case. 133 */ 134 len = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, NULL, 0, NULL, NULL); 135 msgA = heap_xalloc(len); 136 137 WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, msgA, len, NULL, NULL); 138 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE); 139 heap_free(msgA); 140 } 141 } 142 143 static void output_formatstring(const WCHAR *fmt, __ms_va_list va_args) 144 { 145 WCHAR *str; 146 DWORD len; 147 148 SetLastError(NO_ERROR); 149 len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, 150 fmt, 0, 0, (WCHAR *)&str, 0, &va_args); 151 if (len == 0 && GetLastError() != NO_ERROR) 152 { 153 WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt)); 154 return; 155 } 156 output_writeconsole(str, len); 157 LocalFree(str); 158 } 159 160 void WINAPIV output_message(unsigned int id, ...) 161 { 162 WCHAR fmt[1024]; 163 __ms_va_list va_args; 164 165 if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt))) 166 { 167 WINE_FIXME("LoadString failed with %d\n", GetLastError()); 168 return; 169 } 170 __ms_va_start(va_args, id); 171 output_formatstring(fmt, va_args); 172 __ms_va_end(va_args); 173 } 174 175 static void WINAPIV output_string(const WCHAR *fmt, ...) 176 { 177 __ms_va_list va_args; 178 179 __ms_va_start(va_args, fmt); 180 output_formatstring(fmt, va_args); 181 __ms_va_end(va_args); 182 } 183 184 /* ask_confirm() adapted from programs/cmd/builtins.c */ 185 BOOL ask_confirm(unsigned int msgid, WCHAR *reg_info) 186 { 187 HMODULE hmod; 188 WCHAR Ybuffer[4]; 189 WCHAR Nbuffer[4]; 190 WCHAR defval[32]; 191 WCHAR answer[MAX_PATH]; 192 WCHAR *str; 193 DWORD count; 194 195 hmod = GetModuleHandleW(NULL); 196 LoadStringW(hmod, STRING_YES, Ybuffer, ARRAY_SIZE(Ybuffer)); 197 LoadStringW(hmod, STRING_NO, Nbuffer, ARRAY_SIZE(Nbuffer)); 198 LoadStringW(hmod, STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval)); 199 200 str = (reg_info && *reg_info) ? reg_info : defval; 201 202 while (1) 203 { 204 output_message(msgid, str); 205 output_message(STRING_YESNO); 206 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count, NULL); 207 answer[0] = toupperW(answer[0]); 208 if (answer[0] == Ybuffer[0]) 209 return TRUE; 210 if (answer[0] == Nbuffer[0]) 211 return FALSE; 212 } 213 } 214 215 static inline BOOL path_rootname_cmp(const WCHAR *input_path, const WCHAR *rootkey_name) 216 { 217 DWORD length = strlenW(rootkey_name); 218 219 return (!strncmpiW(input_path, rootkey_name, length) && 220 (input_path[length] == 0 || input_path[length] == '\\')); 221 } 222 223 HKEY path_get_rootkey(const WCHAR *path) 224 { 225 DWORD i; 226 227 for (i = 0; i < ARRAY_SIZE(root_rels); i++) 228 { 229 if (path_rootname_cmp(path, root_rels[i].short_name) || 230 path_rootname_cmp(path, root_rels[i].long_name)) 231 return root_rels[i].key; 232 } 233 234 return NULL; 235 } 236 237 static DWORD wchar_get_type(const WCHAR *type_name) 238 { 239 DWORD i; 240 241 if (!type_name) 242 return REG_SZ; 243 244 for (i = 0; i < ARRAY_SIZE(type_rels); i++) 245 { 246 if (!strcmpiW(type_rels[i].name, type_name)) 247 return type_rels[i].type; 248 } 249 250 return ~0u; 251 } 252 253 /* hexchar_to_byte from programs/regedit/hexedit.c */ 254 static inline BYTE hexchar_to_byte(WCHAR ch) 255 { 256 if (ch >= '0' && ch <= '9') 257 return ch - '0'; 258 else if (ch >= 'a' && ch <= 'f') 259 return ch - 'a' + 10; 260 else if (ch >= 'A' && ch <= 'F') 261 return ch - 'A' + 10; 262 else 263 return -1; 264 } 265 266 static LPBYTE get_regdata(const WCHAR *data, DWORD reg_type, WCHAR separator, DWORD *reg_count) 267 { 268 static const WCHAR empty; 269 LPBYTE out_data = NULL; 270 *reg_count = 0; 271 272 if (!data) data = ∅ 273 274 switch (reg_type) 275 { 276 case REG_NONE: 277 case REG_SZ: 278 case REG_EXPAND_SZ: 279 { 280 *reg_count = (lstrlenW(data) + 1) * sizeof(WCHAR); 281 out_data = heap_xalloc(*reg_count); 282 lstrcpyW((LPWSTR)out_data,data); 283 break; 284 } 285 case REG_DWORD: 286 /* case REG_DWORD_LITTLE_ENDIAN: */ 287 case REG_DWORD_BIG_ENDIAN: /* Yes, this is correct! */ 288 { 289 LPWSTR rest; 290 unsigned long val; 291 val = wcstoul(data, &rest, (tolowerW(data[1]) == 'x') ? 16 : 10); 292 if (*rest || data[0] == '-' || (val == ~0u && errno == ERANGE)) { 293 output_message(STRING_MISSING_INTEGER); 294 break; 295 } 296 *reg_count = sizeof(DWORD); 297 out_data = heap_xalloc(*reg_count); 298 ((LPDWORD)out_data)[0] = val; 299 break; 300 } 301 case REG_BINARY: 302 { 303 BYTE hex0, hex1; 304 int i = 0, destByteIndex = 0, datalen = lstrlenW(data); 305 *reg_count = ((datalen + datalen % 2) / 2) * sizeof(BYTE); 306 out_data = heap_xalloc(*reg_count); 307 if(datalen % 2) 308 { 309 hex1 = hexchar_to_byte(data[i++]); 310 if(hex1 == 0xFF) 311 goto no_hex_data; 312 out_data[destByteIndex++] = hex1; 313 } 314 for(;i + 1 < datalen;i += 2) 315 { 316 hex0 = hexchar_to_byte(data[i]); 317 hex1 = hexchar_to_byte(data[i + 1]); 318 if(hex0 == 0xFF || hex1 == 0xFF) 319 goto no_hex_data; 320 out_data[destByteIndex++] = (hex0 << 4) | hex1; 321 } 322 break; 323 no_hex_data: 324 /* cleanup, print error */ 325 heap_free(out_data); 326 output_message(STRING_MISSING_HEXDATA); 327 out_data = NULL; 328 break; 329 } 330 case REG_MULTI_SZ: 331 { 332 int i, destindex, len = strlenW(data); 333 WCHAR *buffer = heap_xalloc((len + 2) * sizeof(WCHAR)); 334 335 for (i = 0, destindex = 0; i < len; i++, destindex++) 336 { 337 if (!separator && data[i] == '\\' && data[i + 1] == '0') 338 { 339 buffer[destindex] = 0; 340 i++; 341 } 342 else if (data[i] == separator) 343 buffer[destindex] = 0; 344 else 345 buffer[destindex] = data[i]; 346 347 if (destindex && !buffer[destindex - 1] && (!buffer[destindex] || destindex == 1)) 348 { 349 heap_free(buffer); 350 output_message(STRING_INVALID_STRING); 351 return NULL; 352 } 353 } 354 buffer[destindex] = 0; 355 if (destindex && buffer[destindex - 1]) 356 buffer[++destindex] = 0; 357 *reg_count = (destindex + 1) * sizeof(WCHAR); 358 return (BYTE *)buffer; 359 } 360 default: 361 output_message(STRING_UNHANDLED_TYPE, reg_type, data); 362 } 363 364 return out_data; 365 } 366 367 static BOOL sane_path(const WCHAR *key) 368 { 369 unsigned int i = strlenW(key); 370 371 if (i < 3 || (key[i - 1] == '\\' && key[i - 2] == '\\')) 372 { 373 output_message(STRING_INVALID_KEY); 374 return FALSE; 375 } 376 377 if (key[0] == '\\' && key[1] == '\\' && key[2] != '\\') 378 { 379 output_message(STRING_NO_REMOTE); 380 return FALSE; 381 } 382 383 return TRUE; 384 } 385 386 static int reg_add(HKEY root, WCHAR *path, WCHAR *value_name, BOOL value_empty, 387 WCHAR *type, WCHAR separator, WCHAR *data, BOOL force) 388 { 389 HKEY key; 390 391 if (RegCreateKeyW(root, path, &key) != ERROR_SUCCESS) 392 { 393 output_message(STRING_INVALID_KEY); 394 return 1; 395 } 396 397 if (value_name || value_empty || data) 398 { 399 DWORD reg_type; 400 DWORD reg_count = 0; 401 BYTE* reg_data = NULL; 402 403 if (!force) 404 { 405 if (RegQueryValueExW(key, value_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) 406 { 407 if (!ask_confirm(STRING_OVERWRITE_VALUE, value_name)) 408 { 409 RegCloseKey(key); 410 output_message(STRING_CANCELLED); 411 return 0; 412 } 413 } 414 } 415 416 reg_type = wchar_get_type(type); 417 if (reg_type == ~0u) 418 { 419 RegCloseKey(key); 420 output_message(STRING_UNSUPPORTED_TYPE, type); 421 return 1; 422 } 423 if ((reg_type == REG_DWORD || reg_type == REG_DWORD_BIG_ENDIAN) && !data) 424 { 425 RegCloseKey(key); 426 output_message(STRING_INVALID_CMDLINE); 427 return 1; 428 } 429 430 if (!(reg_data = get_regdata(data, reg_type, separator, ®_count))) 431 { 432 RegCloseKey(key); 433 return 1; 434 } 435 436 RegSetValueExW(key, value_name, 0, reg_type, reg_data, reg_count); 437 heap_free(reg_data); 438 } 439 440 RegCloseKey(key); 441 output_message(STRING_SUCCESS); 442 443 return 0; 444 } 445 446 static int reg_delete(HKEY root, WCHAR *path, WCHAR *key_name, WCHAR *value_name, 447 BOOL value_empty, BOOL value_all, BOOL force) 448 { 449 HKEY key; 450 451 if (!force) 452 { 453 BOOL ret; 454 455 if (value_name || value_empty) 456 ret = ask_confirm(STRING_DELETE_VALUE, value_name); 457 else if (value_all) 458 ret = ask_confirm(STRING_DELETE_VALUEALL, key_name); 459 else 460 ret = ask_confirm(STRING_DELETE_SUBKEY, key_name); 461 462 if (!ret) 463 { 464 output_message(STRING_CANCELLED); 465 return 0; 466 } 467 } 468 469 /* Delete subtree only if no /v* option is given */ 470 if (!value_name && !value_empty && !value_all) 471 { 472 if (RegDeleteTreeW(root, path) != ERROR_SUCCESS) 473 { 474 output_message(STRING_CANNOT_FIND); 475 return 1; 476 } 477 output_message(STRING_SUCCESS); 478 return 0; 479 } 480 481 if (RegOpenKeyW(root, path, &key) != ERROR_SUCCESS) 482 { 483 output_message(STRING_CANNOT_FIND); 484 return 1; 485 } 486 487 if (value_all) 488 { 489 DWORD max_value_len = 256, value_len; 490 WCHAR *value_name; 491 LONG rc; 492 493 value_name = heap_xalloc(max_value_len * sizeof(WCHAR)); 494 495 while (1) 496 { 497 value_len = max_value_len; 498 rc = RegEnumValueW(key, 0, value_name, &value_len, NULL, NULL, NULL, NULL); 499 if (rc == ERROR_SUCCESS) 500 { 501 rc = RegDeleteValueW(key, value_name); 502 if (rc != ERROR_SUCCESS) 503 { 504 heap_free(value_name); 505 RegCloseKey(key); 506 output_message(STRING_VALUEALL_FAILED, key_name); 507 return 1; 508 } 509 } 510 else if (rc == ERROR_MORE_DATA) 511 { 512 max_value_len *= 2; 513 value_name = heap_xrealloc(value_name, max_value_len * sizeof(WCHAR)); 514 } 515 else break; 516 } 517 heap_free(value_name); 518 } 519 else if (value_name || value_empty) 520 { 521 if (RegDeleteValueW(key, value_empty ? NULL : value_name) != ERROR_SUCCESS) 522 { 523 RegCloseKey(key); 524 output_message(STRING_CANNOT_FIND); 525 return 1; 526 } 527 } 528 529 RegCloseKey(key); 530 output_message(STRING_SUCCESS); 531 return 0; 532 } 533 534 static WCHAR *reg_data_to_wchar(DWORD type, const BYTE *src, DWORD size_bytes) 535 { 536 WCHAR *buffer = NULL; 537 int i; 538 539 switch (type) 540 { 541 case REG_SZ: 542 case REG_EXPAND_SZ: 543 buffer = heap_xalloc(size_bytes); 544 strcpyW(buffer, (WCHAR *)src); 545 break; 546 case REG_NONE: 547 case REG_BINARY: 548 { 549 WCHAR *ptr; 550 static const WCHAR fmt[] = {'%','0','2','X',0}; 551 552 buffer = heap_xalloc((size_bytes * 2 + 1) * sizeof(WCHAR)); 553 ptr = buffer; 554 for (i = 0; i < size_bytes; i++) 555 ptr += sprintfW(ptr, fmt, src[i]); 556 break; 557 } 558 case REG_DWORD: 559 /* case REG_DWORD_LITTLE_ENDIAN: */ 560 case REG_DWORD_BIG_ENDIAN: 561 { 562 const int zero_x_dword = 10; 563 static const WCHAR fmt[] = {'0','x','%','x',0}; 564 565 buffer = heap_xalloc((zero_x_dword + 1) * sizeof(WCHAR)); 566 sprintfW(buffer, fmt, *(DWORD *)src); 567 break; 568 } 569 case REG_MULTI_SZ: 570 { 571 const int two_wchars = 2 * sizeof(WCHAR); 572 DWORD tmp_size; 573 const WCHAR *tmp = (const WCHAR *)src; 574 int len, destindex; 575 576 if (size_bytes <= two_wchars) 577 { 578 buffer = heap_xalloc(sizeof(WCHAR)); 579 *buffer = 0; 580 return buffer; 581 } 582 583 tmp_size = size_bytes - two_wchars; /* exclude both null terminators */ 584 buffer = heap_xalloc(tmp_size * 2 + sizeof(WCHAR)); 585 len = tmp_size / sizeof(WCHAR); 586 587 for (i = 0, destindex = 0; i < len; i++, destindex++) 588 { 589 if (tmp[i]) 590 buffer[destindex] = tmp[i]; 591 else 592 { 593 buffer[destindex++] = '\\'; 594 buffer[destindex] = '0'; 595 } 596 } 597 buffer[destindex] = 0; 598 break; 599 } 600 } 601 return buffer; 602 } 603 604 static const WCHAR *reg_type_to_wchar(DWORD type) 605 { 606 int i, array_size = ARRAY_SIZE(type_rels); 607 608 for (i = 0; i < array_size; i++) 609 { 610 if (type == type_rels[i].type) 611 return type_rels[i].name; 612 } 613 return NULL; 614 } 615 616 static void output_value(const WCHAR *value_name, DWORD type, BYTE *data, DWORD data_size) 617 { 618 static const WCHAR fmt[] = {' ',' ',' ',' ','%','1',0}; 619 WCHAR defval[32]; 620 WCHAR *reg_data; 621 622 if (value_name && value_name[0]) 623 output_string(fmt, value_name); 624 else 625 { 626 LoadStringW(GetModuleHandleW(NULL), STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval)); 627 output_string(fmt, defval); 628 } 629 output_string(fmt, reg_type_to_wchar(type)); 630 631 if (data) 632 { 633 reg_data = reg_data_to_wchar(type, data, data_size); 634 output_string(fmt, reg_data); 635 heap_free(reg_data); 636 } 637 else 638 { 639 LoadStringW(GetModuleHandleW(NULL), STRING_VALUE_NOT_SET, defval, ARRAY_SIZE(defval)); 640 output_string(fmt, defval); 641 } 642 output_string(newlineW); 643 } 644 645 WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len) 646 { 647 WCHAR *subkey_path; 648 static const WCHAR fmt[] = {'%','s','\\','%','s',0}; 649 650 subkey_path = heap_xalloc((path_len + subkey_len + 2) * sizeof(WCHAR)); 651 sprintfW(subkey_path, fmt, path, subkey_name); 652 653 return subkey_path; 654 } 655 656 static unsigned int num_values_found = 0; 657 658 static int query_value(HKEY key, WCHAR *value_name, WCHAR *path, BOOL recurse) 659 { 660 LONG rc; 661 DWORD max_data_bytes = 2048, data_size; 662 DWORD subkey_len; 663 DWORD type, path_len, i; 664 BYTE *data; 665 WCHAR fmt[] = {'%','1','\n',0}; 666 WCHAR *subkey_name, *subkey_path; 667 HKEY subkey; 668 669 data = heap_xalloc(max_data_bytes); 670 671 for (;;) 672 { 673 data_size = max_data_bytes; 674 rc = RegQueryValueExW(key, value_name, NULL, &type, data, &data_size); 675 if (rc == ERROR_MORE_DATA) 676 { 677 max_data_bytes = data_size; 678 data = heap_xrealloc(data, max_data_bytes); 679 } 680 else break; 681 } 682 683 if (rc == ERROR_SUCCESS) 684 { 685 output_string(fmt, path); 686 output_value(value_name, type, data, data_size); 687 output_string(newlineW); 688 num_values_found++; 689 } 690 691 heap_free(data); 692 693 if (!recurse) 694 { 695 if (rc == ERROR_FILE_NOT_FOUND) 696 { 697 if (value_name && *value_name) 698 { 699 output_message(STRING_CANNOT_FIND); 700 return 1; 701 } 702 output_string(fmt, path); 703 output_value(NULL, REG_SZ, NULL, 0); 704 } 705 return 0; 706 } 707 708 subkey_name = heap_xalloc(MAX_SUBKEY_LEN * sizeof(WCHAR)); 709 710 path_len = strlenW(path); 711 712 i = 0; 713 for (;;) 714 { 715 subkey_len = MAX_SUBKEY_LEN; 716 rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL); 717 if (rc == ERROR_SUCCESS) 718 { 719 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len); 720 if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey)) 721 { 722 query_value(subkey, value_name, subkey_path, recurse); 723 RegCloseKey(subkey); 724 } 725 heap_free(subkey_path); 726 i++; 727 } 728 else break; 729 } 730 731 heap_free(subkey_name); 732 return 0; 733 } 734 735 static int query_all(HKEY key, WCHAR *path, BOOL recurse) 736 { 737 LONG rc; 738 DWORD max_value_len = 256, value_len; 739 DWORD max_data_bytes = 2048, data_size; 740 DWORD subkey_len; 741 DWORD i, type, path_len; 742 WCHAR fmt[] = {'%','1','\n',0}; 743 WCHAR fmt_path[] = {'%','1','\\','%','2','\n',0}; 744 WCHAR *value_name, *subkey_name, *subkey_path; 745 BYTE *data; 746 HKEY subkey; 747 748 output_string(fmt, path); 749 750 value_name = heap_xalloc(max_value_len * sizeof(WCHAR)); 751 data = heap_xalloc(max_data_bytes); 752 753 i = 0; 754 for (;;) 755 { 756 value_len = max_value_len; 757 data_size = max_data_bytes; 758 rc = RegEnumValueW(key, i, value_name, &value_len, NULL, &type, data, &data_size); 759 if (rc == ERROR_SUCCESS) 760 { 761 output_value(value_name, type, data, data_size); 762 i++; 763 } 764 else if (rc == ERROR_MORE_DATA) 765 { 766 if (data_size > max_data_bytes) 767 { 768 max_data_bytes = data_size; 769 data = heap_xrealloc(data, max_data_bytes); 770 } 771 else 772 { 773 max_value_len *= 2; 774 value_name = heap_xrealloc(value_name, max_value_len * sizeof(WCHAR)); 775 } 776 } 777 else break; 778 } 779 780 heap_free(data); 781 heap_free(value_name); 782 783 if (i || recurse) 784 output_string(newlineW); 785 786 subkey_name = heap_xalloc(MAX_SUBKEY_LEN * sizeof(WCHAR)); 787 788 path_len = strlenW(path); 789 790 i = 0; 791 for (;;) 792 { 793 subkey_len = MAX_SUBKEY_LEN; 794 rc = RegEnumKeyExW(key, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL); 795 if (rc == ERROR_SUCCESS) 796 { 797 if (recurse) 798 { 799 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len); 800 if (!RegOpenKeyExW(key, subkey_name, 0, KEY_READ, &subkey)) 801 { 802 query_all(subkey, subkey_path, recurse); 803 RegCloseKey(subkey); 804 } 805 heap_free(subkey_path); 806 } 807 else output_string(fmt_path, path, subkey_name); 808 i++; 809 } 810 else break; 811 } 812 813 heap_free(subkey_name); 814 815 if (i && !recurse) 816 output_string(newlineW); 817 818 return 0; 819 } 820 821 static int reg_query(HKEY root, WCHAR *path, WCHAR *key_name, WCHAR *value_name, 822 BOOL value_empty, BOOL recurse) 823 { 824 HKEY key; 825 int ret; 826 827 if (RegOpenKeyExW(root, path, 0, KEY_READ, &key) != ERROR_SUCCESS) 828 { 829 output_message(STRING_CANNOT_FIND); 830 return 1; 831 } 832 833 output_string(newlineW); 834 835 if (value_name || value_empty) 836 { 837 ret = query_value(key, value_name, key_name, recurse); 838 if (recurse) 839 output_message(STRING_MATCHES_FOUND, num_values_found); 840 } 841 else 842 ret = query_all(key, key_name, recurse); 843 844 RegCloseKey(key); 845 846 return ret; 847 } 848 849 static WCHAR *get_long_key(HKEY root, WCHAR *path) 850 { 851 DWORD i, array_size = ARRAY_SIZE(root_rels), len; 852 WCHAR *long_key; 853 WCHAR fmt[] = {'%','s','\\','%','s',0}; 854 855 for (i = 0; i < array_size; i++) 856 { 857 if (root == root_rels[i].key) 858 break; 859 } 860 861 len = strlenW(root_rels[i].long_name); 862 863 if (!path) 864 { 865 long_key = heap_xalloc((len + 1) * sizeof(WCHAR)); 866 strcpyW(long_key, root_rels[i].long_name); 867 return long_key; 868 } 869 870 len += strlenW(path) + 1; /* add one for the backslash */ 871 long_key = heap_xalloc((len + 1) * sizeof(WCHAR)); 872 sprintfW(long_key, fmt, root_rels[i].long_name, path); 873 return long_key; 874 } 875 876 BOOL parse_registry_key(const WCHAR *key, HKEY *root, WCHAR **path, WCHAR **long_key) 877 { 878 if (!sane_path(key)) 879 return FALSE; 880 881 *path = strchrW(key, '\\'); 882 if (*path) (*path)++; 883 884 *root = path_get_rootkey(key); 885 if (!*root) 886 { 887 if (*path) *(*path - 1) = 0; 888 output_message(STRING_INVALID_SYSTEM_KEY, key); 889 return FALSE; 890 } 891 892 *long_key = get_long_key(*root, *path); 893 894 return TRUE; 895 } 896 897 static BOOL is_switch(const WCHAR *s, const WCHAR c) 898 { 899 if (strlenW(s) > 2) 900 return FALSE; 901 902 if ((s[0] == '/' || s[0] == '-') && (s[1] == c || s[1] == toupperW(c))) 903 return TRUE; 904 905 return FALSE; 906 } 907 908 static BOOL is_help_switch(const WCHAR *s) 909 { 910 if (is_switch(s, '?') || is_switch(s, 'h')) 911 return TRUE; 912 913 return FALSE; 914 } 915 916 enum operations { 917 REG_ADD, 918 REG_DELETE, 919 REG_IMPORT, 920 REG_EXPORT, 921 REG_QUERY, 922 REG_INVALID 923 }; 924 925 static enum operations get_operation(const WCHAR *str, int *op_help) 926 { 927 struct op_info { const WCHAR *op; int id; int help_id; }; 928 929 static const WCHAR add[] = {'a','d','d',0}; 930 static const WCHAR delete[] = {'d','e','l','e','t','e',0}; 931 static const WCHAR import[] = {'i','m','p','o','r','t',0}; 932 static const WCHAR export[] = {'e','x','p','o','r','t',0}; 933 static const WCHAR query[] = {'q','u','e','r','y',0}; 934 935 static const struct op_info op_array[] = 936 { 937 { add, REG_ADD, STRING_ADD_USAGE }, 938 { delete, REG_DELETE, STRING_DELETE_USAGE }, 939 { import, REG_IMPORT, STRING_IMPORT_USAGE }, 940 { export, REG_EXPORT, STRING_EXPORT_USAGE }, 941 { query, REG_QUERY, STRING_QUERY_USAGE }, 942 { NULL, -1, 0 } 943 }; 944 945 const struct op_info *ptr; 946 947 for (ptr = op_array; ptr->op; ptr++) 948 { 949 if (!lstrcmpiW(str, ptr->op)) 950 { 951 *op_help = ptr->help_id; 952 return ptr->id; 953 } 954 } 955 956 return REG_INVALID; 957 } 958 959 int wmain(int argc, WCHAR *argvW[]) 960 { 961 int i, op, op_help, ret; 962 BOOL show_op_help = FALSE; 963 static const WCHAR switchVAW[] = {'v','a',0}; 964 static const WCHAR switchVEW[] = {'v','e',0}; 965 WCHAR *key_name, *path, *value_name = NULL, *type = NULL, *data = NULL, separator = '\0'; 966 BOOL value_empty = FALSE, value_all = FALSE, recurse = FALSE, force = FALSE; 967 HKEY root; 968 969 if (argc == 1) 970 { 971 output_message(STRING_INVALID_SYNTAX); 972 output_message(STRING_REG_HELP); 973 return 1; 974 } 975 976 if (is_help_switch(argvW[1])) 977 { 978 output_message(STRING_USAGE); 979 return 0; 980 } 981 982 op = get_operation(argvW[1], &op_help); 983 984 if (op == REG_INVALID) 985 { 986 output_message(STRING_INVALID_OPTION, argvW[1]); 987 output_message(STRING_REG_HELP); 988 return 1; 989 } 990 991 if (argc > 2) 992 show_op_help = is_help_switch(argvW[2]); 993 994 if (argc == 2 || ((show_op_help || op == REG_IMPORT) && argc > 3)) 995 { 996 output_message(STRING_INVALID_SYNTAX); 997 output_message(STRING_FUNC_HELP, struprW(argvW[1])); 998 return 1; 999 } 1000 else if (show_op_help) 1001 { 1002 output_message(op_help); 1003 return 0; 1004 } 1005 1006 if (op == REG_IMPORT) 1007 return reg_import(argvW[2]); 1008 1009 if (op == REG_EXPORT) 1010 return reg_export(argc, argvW); 1011 1012 if (!parse_registry_key(argvW[2], &root, &path, &key_name)) 1013 return 1; 1014 1015 for (i = 3; i < argc; i++) 1016 { 1017 if (argvW[i][0] == '/' || argvW[i][0] == '-') 1018 { 1019 WCHAR *ptr = &argvW[i][1]; 1020 1021 if (!lstrcmpiW(ptr, switchVEW)) 1022 { 1023 value_empty = TRUE; 1024 continue; 1025 } 1026 else if (!lstrcmpiW(ptr, switchVAW)) 1027 { 1028 value_all = TRUE; 1029 continue; 1030 } 1031 else if (!ptr[0] || ptr[1]) 1032 { 1033 output_message(STRING_INVALID_CMDLINE); 1034 return 1; 1035 } 1036 1037 switch(tolowerW(argvW[i][1])) 1038 { 1039 case 'v': 1040 if (value_name || !(value_name = argvW[++i])) 1041 { 1042 output_message(STRING_INVALID_CMDLINE); 1043 return 1; 1044 } 1045 break; 1046 case 't': 1047 if (type || !(type = argvW[++i])) 1048 { 1049 output_message(STRING_INVALID_CMDLINE); 1050 return 1; 1051 } 1052 break; 1053 case 'd': 1054 if (data || !(data = argvW[++i])) 1055 { 1056 output_message(STRING_INVALID_CMDLINE); 1057 return 1; 1058 } 1059 break; 1060 case 's': 1061 if (op == REG_QUERY) 1062 { 1063 recurse = TRUE; 1064 break; 1065 } 1066 1067 ptr = argvW[++i]; 1068 if (!ptr || strlenW(ptr) != 1) 1069 { 1070 output_message(STRING_INVALID_CMDLINE); 1071 return 1; 1072 } 1073 separator = ptr[0]; 1074 break; 1075 case 'f': 1076 force = TRUE; 1077 break; 1078 default: 1079 output_message(STRING_INVALID_CMDLINE); 1080 return 1; 1081 } 1082 } 1083 } 1084 1085 if ((value_name && value_empty) || (value_name && value_all) || (value_empty && value_all)) 1086 { 1087 output_message(STRING_INVALID_CMDLINE); 1088 return 1; 1089 } 1090 1091 if (op == REG_ADD) 1092 ret = reg_add(root, path, value_name, value_empty, type, separator, data, force); 1093 else if (op == REG_DELETE) 1094 ret = reg_delete(root, path, key_name, value_name, value_empty, value_all, force); 1095 else 1096 ret = reg_query(root, path, key_name, value_name, value_empty, recurse); 1097 return ret; 1098 } 1099