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