Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2840 lines
70 KiB

  1. /*******************************************************************************
  2. *
  3. * (C) COPYRIGHT MICROSOFT CORP., 1993-2000
  4. *
  5. * TITLE: REGPORTE.C
  6. *
  7. * VERSION: 5.00
  8. *
  9. * AUTHOR: Tracy Sharpe
  10. *
  11. * DATE: 06 Apr 1994
  12. *
  13. * File import and export engine routines for the Registry Editor.
  14. *
  15. *******************************************************************************/
  16. #include "pch.h"
  17. #include "regresid.h"
  18. #include "reg1632.h"
  19. #include "regedit.h"
  20. #include "regkey.h"
  21. #include "regfile.h"
  22. #include "regedit.h"
  23. #include <malloc.h>
  24. // Association between the ASCII name and the handle of the registry key.
  25. const REGISTRY_ROOT g_RegistryRoots[] = {
  26. TEXT("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT, PREDEFINE_KEY_CLASSES_ROOT,
  27. TEXT("HKEY_CURRENT_USER"), HKEY_CURRENT_USER, PREDEFINE_KEY_CURRENT_USER,
  28. TEXT("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE, PREDEFINE_KEY_LOCAL_MACHINE,
  29. TEXT("HKEY_USERS"), HKEY_USERS, PREDEFINE_KEY_USERS,
  30. TEXT("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG, PREDEFINE_KEY_CURRENT_CONFIG,
  31. TEXT("HKEY_DYN_DATA"), HKEY_DYN_DATA, 0
  32. };
  33. const TCHAR s_RegistryHeader[] = TEXT("REGEDIT");
  34. const TCHAR s_OldWin31RegFileRoot[] = TEXT(".classes");
  35. const TCHAR s_Win40RegFileHeader[] = TEXT("REGEDIT4\n\n");
  36. #ifdef UNICODE
  37. //
  38. // New header is required for version 5.0 because the version detection code
  39. // in Win 4.0 regedit wasn't very good (See comments in ImportRegFile for
  40. // details)
  41. //
  42. const WORD s_UnicodeByteOrderMark = 0xFEFF;
  43. const TCHAR s_WinNT50RegFileHeader[] = TEXT("Windows Registry Editor Version");
  44. const TCHAR s_WinNT50RegFileVersion[] = TEXT("5.00");
  45. #endif
  46. const TCHAR s_HexPrefix[] = TEXT("hex");
  47. const TCHAR s_DwordPrefix[] = TEXT("dword:");
  48. const TCHAR g_HexConversion[16] = {TEXT('0'), TEXT('1'), TEXT('2'), TEXT('3'), TEXT('4'),
  49. TEXT('5'), TEXT('6'), TEXT('7'), TEXT('8'), TEXT('9'),
  50. TEXT('a'), TEXT('b'), TEXT('c'), TEXT('d'), TEXT('e'), TEXT('f')};
  51. const TCHAR s_FileLineBreak[] = TEXT(",\\\n ");
  52. extern BOOL g_fMultiLineStrings;
  53. //REARCHITECT - we upped the size of this buffer from 512 to 64K to reduce the chance of hitting the bug
  54. //where a DBCS character is split across two buffers. The true fix was too risky at the time.
  55. //Changed for NT5 RC2
  56. #define SIZE_FILE_IO_BUFFER 0x10000 //64K
  57. typedef struct _FILE_IO{
  58. #ifdef UNICODE
  59. //
  60. // Space for unicode/ansi conversions, assumes worst case
  61. // where every unicode char is a double-byte char
  62. //
  63. CHAR ConversionBuffer[SIZE_FILE_IO_BUFFER*2];
  64. #endif
  65. TCHAR Buffer[SIZE_FILE_IO_BUFFER];
  66. FILE_HANDLE hFile;
  67. int BufferOffset;
  68. int CurrentColumn;
  69. int CharsAvailable;
  70. DWORD FileSizeDiv100;
  71. DWORD FileOffset;
  72. UINT LastPercentage;
  73. #ifdef DEBUG
  74. BOOL fValidateUngetChar;
  75. #endif
  76. } FILE_IO;
  77. FILE_IO s_FileIo;
  78. UINT g_FileErrorStringID;
  79. UINT g_ImportFileVersion;
  80. BOOL s_fTreatFileAsUnicode = TRUE;
  81. VOID ImportWin31RegFile(HTREEITEM hComputerItem);
  82. VOID
  83. NEAR PASCAL
  84. ImportNewerRegFile(HTREEITEM hComputerItem);
  85. VOID ParseHeader(LPHKEY lphKey, HTREEITEM hComputerItem);
  86. VOID
  87. NEAR PASCAL
  88. ParseValue(
  89. HKEY hKey,
  90. LPCTSTR lpszValueName
  91. );
  92. VOID
  93. NEAR PASCAL
  94. ParseValuename(
  95. HKEY hKey
  96. );
  97. BOOL
  98. NEAR PASCAL
  99. ParseString(LPPORTVALUEPARAM pPortValueParam);
  100. BOOL
  101. NEAR PASCAL
  102. ParseHexSequence(LPPORTVALUEPARAM pPortValueParam);
  103. BOOL
  104. NEAR PASCAL
  105. ParseHexDword(
  106. LPDWORD lpDword
  107. );
  108. BOOL
  109. NEAR PASCAL
  110. ParseHexByte(
  111. LPBYTE lpByte
  112. );
  113. BOOL
  114. NEAR PASCAL
  115. ParseHexDigit(
  116. LPBYTE lpDigit
  117. );
  118. BOOL
  119. NEAR PASCAL
  120. ParseEndOfLine(
  121. VOID
  122. );
  123. VOID
  124. NEAR PASCAL
  125. SkipWhitespace(
  126. VOID
  127. );
  128. VOID
  129. NEAR PASCAL
  130. SkipPastEndOfLine(
  131. VOID
  132. );
  133. BOOL
  134. NEAR PASCAL
  135. GetChar(
  136. PTCHAR lpChar
  137. );
  138. VOID
  139. NEAR PASCAL
  140. UngetChar(
  141. VOID
  142. );
  143. BOOL
  144. NEAR PASCAL
  145. MatchChar(
  146. TCHAR CharToMatch
  147. );
  148. BOOL
  149. NEAR PASCAL
  150. IsWhitespace(
  151. TCHAR Char
  152. );
  153. BOOL
  154. NEAR PASCAL
  155. IsNewLine(
  156. TCHAR Char
  157. );
  158. VOID
  159. NEAR PASCAL
  160. PutBranch(
  161. HKEY hKey,
  162. LPTSTR lpKeyName
  163. );
  164. VOID
  165. NEAR PASCAL
  166. PutLiteral(
  167. LPCTSTR lpString
  168. );
  169. VOID
  170. NEAR PASCAL
  171. PutString(
  172. LPCTSTR lpString
  173. );
  174. VOID
  175. NEAR PASCAL
  176. PutBinary(
  177. CONST BYTE FAR* lpBuffer,
  178. DWORD Type,
  179. DWORD cbBytes
  180. );
  181. VOID
  182. NEAR PASCAL
  183. PutDword(
  184. DWORD Dword,
  185. BOOL fLeadingZeroes
  186. );
  187. VOID
  188. NEAR PASCAL
  189. PutChar(
  190. TCHAR Char
  191. );
  192. VOID
  193. NEAR PASCAL
  194. FlushIoBuffer(
  195. VOID
  196. );
  197. //------------------------------------------------------------------------------
  198. // Regedit_GetRootKeyFromComputer
  199. //
  200. // DESCRIPTION: Invokes the security editor for the currently selected item.
  201. //
  202. // PARAMETERS: hWnd - handle to the current window
  203. //------------------------------------------------------------------------------
  204. HKEY Regedit_GetRootKeyFromComputer(HTREEITEM hComputerItem, PTSTR pszFullKeyName)
  205. {
  206. HKEY hRootKey = NULL;
  207. if (hComputerItem == NULL)
  208. {
  209. int i;
  210. for (i = 0; i < NUMBER_REGISTRY_ROOTS; i++)
  211. {
  212. if (STRCMP(g_RegistryRoots[i].lpKeyName, pszFullKeyName) == 0)
  213. {
  214. hRootKey = g_RegistryRoots[i].hKey;
  215. break;
  216. }
  217. }
  218. }
  219. else
  220. {
  221. TCHAR acComputerName[MAXKEYNAME];
  222. TV_ITEM TVItem;
  223. // walk through each of the registry roots to see if this key
  224. // belongs to it
  225. HTREEITEM hRegistryRoot =
  226. TreeView_GetChild(g_RegEditData.hKeyTreeWnd, hComputerItem);
  227. TVItem.mask = TVIF_TEXT | TVIF_PARAM;
  228. TVItem.hItem = hRegistryRoot;
  229. TVItem.pszText = acComputerName;
  230. TVItem.cchTextMax = sizeof(acComputerName)/sizeof(TCHAR);
  231. while (hRegistryRoot != NULL)
  232. {
  233. TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
  234. if (STRCMP(acComputerName, pszFullKeyName) == 0)
  235. {
  236. hRootKey = (HKEY)TVItem.lParam;
  237. break;
  238. }
  239. hRegistryRoot =
  240. TreeView_GetNextSibling(g_RegEditData.hKeyTreeWnd, hRegistryRoot);
  241. TVItem.hItem = hRegistryRoot;
  242. }
  243. }
  244. return hRootKey;
  245. }
  246. /*******************************************************************************
  247. *
  248. * EditRegistryKey
  249. *
  250. * DESCRIPTION:
  251. * Parses the pFullKeyName string and creates a handle to the registry key.
  252. *
  253. * PARAMETERS:
  254. * lphKey, location to store handle to registry key.
  255. * lpFullKeyName, string of form "HKEY_LOCAL_MACHINE\Subkey1\Subkey2".
  256. * fCreate, TRUE if key should be created, else FALSE for open only.
  257. * (returns), ERROR_SUCCESS, no errors occurred, phKey is valid,
  258. * ERROR_CANTOPEN, registry access error of some form,
  259. * ERROR_BADKEY, incorrectly formed pFullKeyName.
  260. *
  261. *******************************************************************************/
  262. DWORD EditRegistryKey(HTREEITEM hComputerItem, LPHKEY lphKey, LPTSTR lpFullKeyName, UINT uOperation)
  263. {
  264. LPTSTR lpSubKeyName;
  265. TCHAR PrevChar;
  266. UINT Counter;
  267. DWORD Result;
  268. HKEY hRootKey = NULL;
  269. if ((lpSubKeyName = (LPTSTR) STRCHR(lpFullKeyName, TEXT('\\'))) != NULL) {
  270. PrevChar = *lpSubKeyName;
  271. *lpSubKeyName++ = 0;
  272. }
  273. CHARUPPERSTRING(lpFullKeyName);
  274. hRootKey = Regedit_GetRootKeyFromComputer(hComputerItem, lpFullKeyName);
  275. if (hRootKey)
  276. {
  277. Result = ERROR_CANTOPEN;
  278. switch (uOperation)
  279. {
  280. case ERK_CREATE:
  281. //
  282. // If trying to open one of these keys just return OK
  283. // When lpSubKeyName is NULL, you recreate the parent key
  284. // Since these keys are usually in use this will fail
  285. // This code path only occurs when restoring a whole root key
  286. // from a .reg file.
  287. //
  288. if (((hRootKey == HKEY_LOCAL_MACHINE) || (hRootKey == HKEY_USERS))
  289. && lpSubKeyName == NULL) {
  290. Result = ERROR_SUCCESS;
  291. }
  292. else if (RegCreateKey(hRootKey, lpSubKeyName, lphKey) == ERROR_SUCCESS)
  293. Result = ERROR_SUCCESS;
  294. break;
  295. case ERK_OPEN:
  296. //
  297. // Used when exporting.
  298. //
  299. if(RegOpenKeyEx(hRootKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,lphKey) == ERROR_SUCCESS)
  300. Result = ERROR_SUCCESS;
  301. break;
  302. case ERK_DELETE:
  303. RegDeleteKeyRecursive(hRootKey, lpSubKeyName);
  304. // asssume success... don't care if this fails
  305. Result = ERROR_SUCCESS;
  306. *lphKey = NULL;
  307. break;
  308. }
  309. }
  310. else
  311. {
  312. Result = ERROR_BADKEY;
  313. }
  314. if (lpSubKeyName != NULL) {
  315. lpSubKeyName--;
  316. *lpSubKeyName = PrevChar;
  317. }
  318. return Result;
  319. }
  320. /*******************************************************************************
  321. *
  322. * ImportRegFile
  323. *
  324. * DESCRIPTION:
  325. *
  326. * PARAMETERS:
  327. * lpFileName, address of name of file to be imported.
  328. *
  329. *******************************************************************************/
  330. VOID
  331. PASCAL
  332. ImportRegFile(HWND hWnd, LPTSTR lpFileName, HTREEITEM hComputerItem)
  333. {
  334. TCHAR Char;
  335. LPCTSTR lpHeader;
  336. BOOL fNewRegistryFile;
  337. #ifdef UNICODE
  338. UINT Temp, i;
  339. TCHAR StrToIntBuf[2];
  340. LPCTSTR lp50Header;
  341. #endif // UNICODE
  342. DWORD cch;
  343. TCHAR tchBuffer[MAX_PATH] = {0};
  344. LPTSTR lpFilePart;
  345. g_FileErrorStringID = IDS_IMPFILEERRSUCCESS;
  346. // OPENREADFILE used to be OpenFile(), but there isn't any Unicode version
  347. // of that API, so now it's CreateFile(). But OpenFile searched the path
  348. // automatically, whereas CreateFile does not. Corel's 'Perfect Office v6'
  349. // install app depends on the path being searched, so do it manually.
  350. cch = SearchPath(NULL, // pointer to search path
  351. lpFileName, // pointer to filename
  352. NULL, // pointer to extension
  353. ARRAYSIZE(tchBuffer), // size, in characters, of buffer
  354. (TCHAR*)tchBuffer, // pointer to buffer for found filename
  355. &lpFilePart); // pointer to pointer to file component);
  356. if ((cch != 0) && (cch <= MAX_PATH) && OPENREADFILE((TCHAR*)tchBuffer, s_FileIo.hFile)) {
  357. WORD wBOM;
  358. DWORD NumberOfBytesRead;
  359. s_FileIo.FileSizeDiv100 = GETFILESIZE(s_FileIo.hFile) / 100;
  360. s_FileIo.FileOffset = 0;
  361. s_FileIo.CharsAvailable = 0;
  362. s_FileIo.LastPercentage = 0;
  363. //
  364. // Read the first two bytes. If it's the Unicode byte order mark,
  365. // set a flag so all the rest of the file will be interpreted
  366. // as ANSI or Unicode text properly.
  367. //
  368. if (!READFILE(s_FileIo.hFile, &wBOM,
  369. sizeof(wBOM), &NumberOfBytesRead)) {
  370. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  371. goto exit_gracefully;
  372. }
  373. if (wBOM == s_UnicodeByteOrderMark)
  374. s_fTreatFileAsUnicode = TRUE;
  375. else {
  376. s_fTreatFileAsUnicode = FALSE;
  377. // We probably just read "RE" from "REGEDIT4". Back up the file
  378. // position so the ANSI import routines get what they expect
  379. SetFilePointer(s_FileIo.hFile, -2, NULL, FILE_CURRENT);
  380. }
  381. //
  382. // The following will force GetChar to read in the first block of data.
  383. //
  384. s_FileIo.BufferOffset = 0;
  385. SkipWhitespace();
  386. lpHeader = s_RegistryHeader;
  387. g_ImportFileVersion = 0;
  388. # if 0
  389. Sit back, and I will tell ye a tale of woe.
  390. Win95 and NT 4 shipped with regedit compiled ANSI. There are a couple
  391. of registry types on NT (namely REG_EXPAND_SZ and REG_MULTI_SZ) that
  392. weren't on Win95, and which regedit doesn't really understand. regedit
  393. treats these registry types as hex binary streams.
  394. You can probably see where this is going.
  395. If you exported, say your user TEMP environment variable on NT 4
  396. using regedit, you'd get something that looked like this:
  397. REGEDIT4
  398. [HKEY_CURRENT_USER\Environment]
  399. "TEMP"=hex(2):25,53,59,53,54,45,4d,44,52,49,56,45,25,5c,53,48,54,65,6d,70,00
  400. ...a nice, null-terminated ANSI string. Nice, that is, until we decided
  401. to compile regedit UNICODE for NT 5. A unicode regedit exports your
  402. user TEMP variable like this:
  403. REGEDIT4
  404. [HKEY_CURRENT_USER\Environment]
  405. "TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
  406. 00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
  407. ...mmmm. Unicode. Of course, a unicode regedit also expects anything
  408. it imports to have all those interspersed zeroes, too. Otherwise,
  409. it dumps garbage into your registry. All it takes is a -DUNICODE, and
  410. regedit is suddenly incompatible with the thousdands of existing .reg
  411. files out there.
  412. So just bump the version in the header to REGEDIT5 and be done with
  413. it, right? Wrong. The regedit on Win95 and NT 4 looks at the first
  414. character after the string "REGEDIT" and compares it to the digit "4".
  415. If that character is anything other than the digit "4", the parser
  416. assumes it is looking at a Windows 3.1 file. Yep. There will only
  417. ever be two formats, right? Just Win95 and Win3.1. That's all the
  418. world needs.
  419. So a completely new .reg file header had to be invented, so that the
  420. older, regedits of the world would simply regect the new,
  421. unicodized .reg files outright. An NT 5 .reg file, exporting your user
  422. TEMP variable, looks like this:
  423. Windows Registry Editor Version 5.00
  424. [HKEY_CURRENT_USER\Environment]
  425. "TEMP"=hex(2):25,00,53,00,59,00,53,00,54,00,45,00,4d,00,44,00,52,00,49,00,56,\
  426. 00,45,00,25,00,5c,00,53,00,48,00,54,00,65,00,6d,00,70,00,00,00
  427. The parser is still not very good, but it does bother to convert that 5.00
  428. into a version number, so that future generations can bump it to 5.50 or
  429. 6.00, and the regedit 5.00 that shipped with NT 5.00 will properly reject
  430. the files.
  431. #endif // 0
  432. #ifdef UNICODE
  433. //
  434. // Compare to the new .reg file header
  435. //
  436. lp50Header = s_WinNT50RegFileHeader;
  437. while (*lp50Header != 0) {
  438. if (MatchChar(*lp50Header))
  439. lp50Header = CharNext(lp50Header);
  440. else
  441. break;
  442. }
  443. //
  444. // If the above loop pushed lp50Header to its terminating null
  445. // character, then the header matches.
  446. //
  447. if (0 == *lp50Header) {
  448. SkipWhitespace();
  449. //
  450. // Now, decode the version number into a hex, _WIN32_WINNT
  451. // style version number.
  452. //
  453. StrToIntBuf[1] = 0;
  454. //
  455. // Any number of digits can come before the decimal point
  456. //
  457. while (!MatchChar(TEXT('.'))) {
  458. if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
  459. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  460. goto exit_gracefully;
  461. }
  462. Temp = StrToInt(StrToIntBuf);
  463. // Hex version number, so move left four bits.
  464. g_ImportFileVersion <<= 4;
  465. g_ImportFileVersion += Temp;
  466. }
  467. //
  468. // Fixed at two digits after the decimal point
  469. //
  470. for (i = 0; i < 2; i++) {
  471. if (!GetChar(StrToIntBuf) || !IsCharAlphaNumeric(*StrToIntBuf)) {
  472. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  473. goto exit_gracefully;
  474. }
  475. Temp = StrToInt(StrToIntBuf);
  476. // Hex version number, so move left four bits.
  477. g_ImportFileVersion <<= 4;
  478. g_ImportFileVersion += Temp;
  479. }
  480. //
  481. // For NT 5, reject any version number that isn't
  482. // 5. This can be expanded into a switch statement
  483. // when the version number is bumped later.
  484. //
  485. if (0x0500 != g_ImportFileVersion) {
  486. g_FileErrorStringID = IDS_IMPFILEERRVERBAD;
  487. goto exit_gracefully;
  488. }
  489. else {
  490. SkipWhitespace();
  491. ImportNewerRegFile(hComputerItem);
  492. }
  493. } // if (0 == *lp50Header)
  494. //
  495. // It doesn't use the new .reg file header, so
  496. // it's not an NT 5.0+ registry file, so use the
  497. // older algorithm to see if it's a valid older registry file
  498. //
  499. else {
  500. #endif // UNICODE
  501. while (*lpHeader != 0) {
  502. if (MatchChar(*lpHeader))
  503. lpHeader = CharNext(lpHeader);
  504. else
  505. break;
  506. }
  507. if (*lpHeader == 0) {
  508. //
  509. // Win95's and NT 4's regedit shipped with this line
  510. // of code. It is the cause of all of the suffering above.
  511. // Notice the incorrect assumption: "If the very next
  512. // character isn't a '4', then we must be reading
  513. // a Windows 3.1 registry file!" Of course there won't
  514. // be a version 5 of regedit. Version 4 was perfect!
  515. //
  516. fNewRegistryFile = MatchChar(TEXT('4'));
  517. SkipWhitespace();
  518. if (GetChar(&Char) && IsNewLine(Char)) {
  519. if (fNewRegistryFile) {
  520. g_ImportFileVersion = 0x0400;
  521. ImportNewerRegFile(hComputerItem);
  522. }
  523. else {
  524. g_ImportFileVersion = 0x0310;
  525. ImportWin31RegFile(hComputerItem);
  526. }
  527. }
  528. }
  529. else if (!hWnd)
  530. {
  531. // only registry script files can be imported without the
  532. // regedit window open
  533. g_FileErrorStringID = IDS_IMPFILEERRNOTASCRPT;
  534. }
  535. else if (!hComputerItem || (hComputerItem ==
  536. RegEdit_GetComputerItem(TreeView_GetSelection(g_RegEditData.hKeyTreeWnd))))
  537. {
  538. // It's not a registry file, but since the selected key is on the computer
  539. // they choose, try to import it as a binary registy key (regedt32 type)
  540. // remove the progress dialog
  541. if (g_hRegProgressWnd != NULL)
  542. {
  543. EnableWindow(hWnd, TRUE);
  544. DestroyWindow(g_hRegProgressWnd);
  545. g_hRegProgressWnd = NULL;
  546. }
  547. RestoreBinaryKeyFile(hWnd, lpFileName);
  548. }
  549. else
  550. {
  551. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  552. }
  553. #ifdef UNICODE
  554. }
  555. #endif // UNICODE
  556. } // if (OPENREADFILE...
  557. else {
  558. { TCHAR buff[250];
  559. wsprintf(buff, L"REGEDIT: CreateFile failed, GetLastError() = %d\n", GetLastError());
  560. OutputDebugString(buff);
  561. }
  562. s_FileIo.hFile = NULL;
  563. g_FileErrorStringID = IDS_IMPFILEERRFILEOPEN;
  564. }
  565. #ifdef UNICODE // Urefd labels generate warnings
  566. exit_gracefully:
  567. #endif // UNICODE
  568. if (s_FileIo.hFile) {
  569. CLOSEFILE(s_FileIo.hFile);
  570. }
  571. }
  572. //------------------------------------------------------------------------------
  573. // ImportBinaryKeyFile
  574. //
  575. // DESCRIPTION: Loads a binary key file at the currently selected key
  576. // This is the format of regedt32.exe saved keys
  577. //
  578. // PARAMETERS: LPTSTR lpFileName - file with the binary key
  579. //------------------------------------------------------------------------------
  580. void RestoreBinaryKeyFile(HWND hWnd, LPTSTR lpFileName)
  581. {
  582. TCHAR achKeyName[MAXKEYNAME];
  583. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  584. KeyTree_GetKeyName(hSelectedTreeItem, achKeyName, ARRAYSIZE(achKeyName));
  585. // confirm that they want to replace the registry key
  586. if (InternalMessageBox(g_hInstance, hWnd,
  587. MAKEINTRESOURCE(IDS_CONFIRMRESTOREKEY), MAKEINTRESOURCE(IDS_CONFIRMRESKEYTITLE),
  588. MB_ICONQUESTION | MB_YESNO , achKeyName) == IDYES)
  589. {
  590. HRESULT hr;
  591. RegEdit_SetPrivilege(SE_RESTORE_NAME, TRUE);
  592. // attempt to load the file as a registry key
  593. if ((hr = RegRestoreKey(g_RegEditData.hCurrentSelectionKey, lpFileName,
  594. REG_FORCE_RESTORE)) != ERROR_SUCCESS)
  595. {
  596. switch (hr)
  597. {
  598. case ERROR_PRIVILEGE_NOT_HELD:
  599. g_FileErrorStringID = IDS_IMPFILEERRNOPRIV;
  600. break;
  601. case ERROR_FILE_NOT_FOUND:
  602. g_FileErrorStringID = IDS_IMPFILEERRNOFILE;
  603. break;
  604. case ERROR_INVALID_HANDLE:
  605. g_FileErrorStringID = IDS_IMPFILEERRINVALID;
  606. break;
  607. default:
  608. g_FileErrorStringID = IDS_IMPFILEERRFORMATBAD;
  609. break;
  610. }
  611. }
  612. RegEdit_SetPrivilege(SE_RESTORE_NAME, FALSE);
  613. }
  614. else
  615. {
  616. g_FileErrorStringID = IDS_IMPFILEERRORCANCEL;
  617. }
  618. }
  619. /*******************************************************************************
  620. *
  621. * ImportWin31RegFile
  622. *
  623. * DESCRIPTION:
  624. * Imports the contents of a Windows 3.1 style registry file into the
  625. * registry.
  626. *
  627. * We scan over the file looking for lines of the following type:
  628. * HKEY_CLASSES_ROOT\keyname = value_data
  629. * HKEY_CLASSES_ROOT\keyname =value_data
  630. * HKEY_CLASSES_ROOT\keyname value_data
  631. * HKEY_CLASSES_ROOT\keyname (null value data)
  632. *
  633. * In all cases, any number of spaces may follow 'keyname'. Although we
  634. * only document the first syntax, the Windows 3.1 Regedit handled all of
  635. * these formats as valid, so this version will as well (fortunately, it
  636. * doesn't make the parsing any more complex!).
  637. *
  638. * Note, we also support replacing HKEY_CLASSES_ROOT with \.classes above
  639. * which must come from some early releases of Windows.
  640. *
  641. * PARAMETERS: HTREEITEM hComputerItem - computer item to receive imported key
  642. *
  643. *******************************************************************************/
  644. VOID ImportWin31RegFile(HTREEITEM hComputerItem)
  645. {
  646. HKEY hKey;
  647. TCHAR Char;
  648. BOOL fSuccess;
  649. LPCTSTR lpClassesRoot;
  650. TCHAR KeyName[MAXKEYNAME];
  651. UINT Index;
  652. //
  653. // Keep an open handle to the classes root. We may prevent some
  654. // unneccessary flushing.
  655. //
  656. hKey = Regedit_GetRootKeyFromComputer(hComputerItem,
  657. g_RegistryRoots[INDEX_HKEY_CLASSES_ROOT].lpKeyName);
  658. if (RegOpenKeyEx(hKey, NULL, 0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS)
  659. {
  660. g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
  661. return;
  662. }
  663. while (TRUE) {
  664. //
  665. // Check for the end of file condition.
  666. //
  667. if (!GetChar(&Char))
  668. break;
  669. UngetChar(); // Not efficient, but works for now.
  670. //
  671. // Match the beginning of the line against one of the two aliases for
  672. // HKEY_CLASSES_ROOT.
  673. //
  674. if (MatchChar(TEXT('\\')))
  675. lpClassesRoot = s_OldWin31RegFileRoot;
  676. else
  677. lpClassesRoot = g_RegistryRoots[INDEX_HKEY_CLASSES_ROOT].lpKeyName;
  678. fSuccess = TRUE;
  679. while (*lpClassesRoot != 0) {
  680. if (!MatchChar(*lpClassesRoot++)) {
  681. fSuccess = FALSE;
  682. break;
  683. }
  684. }
  685. //
  686. // Make sure that we have a backslash seperating one of the aliases
  687. // from the keyname.
  688. //
  689. if (fSuccess)
  690. fSuccess = MatchChar(TEXT('\\'));
  691. if (fSuccess) {
  692. //
  693. // We've found one of the valid aliases, so read in the keyname.
  694. //
  695. // fSuccess = TRUE; // Must be TRUE if we're in this block
  696. Index = 0;
  697. while (GetChar(&Char)) {
  698. if (Char == TEXT(' ') || IsNewLine(Char))
  699. break;
  700. //
  701. // Make sure that the keyname buffer doesn't overflow. We must
  702. // leave room for a terminating null.
  703. //
  704. if (Index >= (sizeof(KeyName)/sizeof(TCHAR)) - 1) {
  705. fSuccess = FALSE;
  706. break;
  707. }
  708. KeyName[Index++] = Char;
  709. }
  710. if (fSuccess)
  711. {
  712. UINT cMaxDataLength = ALLOCATION_INCR;
  713. PBYTE pbValueDataBuffer;
  714. KeyName[Index] = 0;
  715. //
  716. // Now see if we have a value to assign to this keyname.
  717. //
  718. SkipWhitespace();
  719. if (MatchChar(TEXT('=')))
  720. MatchChar(TEXT(' '));
  721. // fSuccess = TRUE; // Must be TRUE if we're in this block
  722. Index = 0;
  723. pbValueDataBuffer = LocalAlloc(LPTR, cMaxDataLength);
  724. fSuccess = (pbValueDataBuffer != NULL);
  725. while (GetChar(&Char) && fSuccess)
  726. {
  727. if (IsNewLine(Char))
  728. break;
  729. //
  730. // Make sure that the value data buffer doesn't overflow.
  731. // Because this is always string data, we must leave room
  732. // for a terminating null.
  733. //
  734. if (Index >= cMaxDataLength - 1)
  735. {
  736. PBYTE pbValueData =
  737. LocalReAlloc(pbValueDataBuffer, cMaxDataLength + ALLOCATION_INCR, LMEM_MOVEABLE);
  738. fSuccess = (pbValueData != NULL);
  739. if (!fSuccess)
  740. {
  741. break;
  742. }
  743. else
  744. {
  745. pbValueDataBuffer = pbValueData;
  746. cMaxDataLength += ALLOCATION_INCR;
  747. }
  748. }
  749. ((PTSTR)pbValueDataBuffer)[Index++] = Char;
  750. }
  751. if (fSuccess)
  752. {
  753. ((PTSTR)pbValueDataBuffer)[Index] = 0;
  754. if (RegSetValue(hKey, KeyName, REG_SZ, (LPCTSTR)pbValueDataBuffer,
  755. Index*sizeof(TCHAR)) != ERROR_SUCCESS)
  756. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  757. }
  758. else
  759. {
  760. g_FileErrorStringID = IDS_NOMEMORY;
  761. }
  762. if (pbValueDataBuffer)
  763. {
  764. LocalFree(pbValueDataBuffer);
  765. }
  766. }
  767. }
  768. //
  769. // Somewhere along the line, we had a parsing error, so resynchronize
  770. // on the next line.
  771. //
  772. if (!fSuccess)
  773. SkipPastEndOfLine();
  774. }
  775. RegFlushKey(hKey);
  776. RegCloseKey(hKey);
  777. }
  778. /*******************************************************************************
  779. *
  780. * ImportNewerRegFile
  781. *
  782. * DESCRIPTION:
  783. *
  784. * PARAMETERS:
  785. *
  786. *******************************************************************************/
  787. VOID
  788. NEAR PASCAL
  789. ImportNewerRegFile(HTREEITEM hComputerItem)
  790. {
  791. HKEY hLocalMachineKey;
  792. HKEY hUsersKey;
  793. HKEY hKey;
  794. TCHAR Char;
  795. #ifdef WINNT
  796. hLocalMachineKey = NULL;
  797. hUsersKey = NULL;
  798. #else
  799. //
  800. // Keep open handles for the predefined roots to prevent the registry
  801. // library from flushing after every single RegOpenKey/RegCloseKey
  802. // operation.
  803. //
  804. RegOpenKey(HKEY_LOCAL_MACHINE, NULL, &hLocalMachineKey);
  805. RegOpenKey(HKEY_USERS, NULL, &hUsersKey);
  806. #ifdef DEBUG
  807. if (hLocalMachineKey == NULL)
  808. DebugPrintf(("Unable to open HKEY_LOCAL_MACHINE\n\r"));
  809. if (hUsersKey == NULL)
  810. DebugPrintf(("Unable to open HKEY_USERS\n\r"));
  811. #endif
  812. #endif
  813. hKey = NULL;
  814. while (TRUE) {
  815. SkipWhitespace();
  816. //
  817. // Check for the end of file condition.
  818. //
  819. if (!GetChar(&Char))
  820. break;
  821. switch (Char) {
  822. case TEXT('['):
  823. //
  824. // If a registry key is currently open, we must close it first.
  825. // If ParseHeader happens to fail (for example, no closing
  826. // bracket), then hKey will be NULL and any values that we
  827. // parse must be ignored.
  828. //
  829. if (hKey != NULL) {
  830. RegCloseKey(hKey);
  831. hKey = NULL;
  832. }
  833. ParseHeader(&hKey, hComputerItem);
  834. break;
  835. case TEXT('"'):
  836. //
  837. // As noted above, if we don't have an open registry key, then
  838. // just skip the line.
  839. //
  840. if (hKey != NULL)
  841. ParseValuename(hKey);
  842. else
  843. SkipPastEndOfLine();
  844. break;
  845. case TEXT('@'):
  846. //
  847. //
  848. //
  849. if (hKey != NULL)
  850. ParseValue(hKey, NULL);
  851. else
  852. SkipPastEndOfLine();
  853. break;
  854. case TEXT(';'):
  855. //
  856. // This line is a comment so just dump the rest of it.
  857. //
  858. SkipPastEndOfLine();
  859. break;
  860. default:
  861. if (IsNewLine(Char))
  862. break;
  863. SkipPastEndOfLine();
  864. break;
  865. }
  866. }
  867. if (hKey != NULL)
  868. RegCloseKey(hKey);
  869. if (hUsersKey != NULL)
  870. RegCloseKey(hUsersKey);
  871. if (hLocalMachineKey != NULL)
  872. RegCloseKey(hLocalMachineKey);
  873. }
  874. /*******************************************************************************
  875. *
  876. * ParseHeader
  877. *
  878. * DESCRIPTION:
  879. *
  880. * PARAMETERS:
  881. *
  882. *******************************************************************************/
  883. // REARCHITECT - each subkeyname can be MAXKEYNAME in size
  884. // ideally - we should handle unlimited size names
  885. // let's at least handle bigger names for now
  886. // at least a depth of 10 with maximum length subkey names
  887. #define SIZE_FULL_KEYNAME ((MAXKEYNAME + 40)*10)
  888. VOID ParseHeader(LPHKEY lphKey, HTREEITEM hComputerItem)
  889. {
  890. TCHAR FullKeyName[SIZE_FULL_KEYNAME];
  891. int CurrentIndex;
  892. int LastRightBracketIndex;
  893. TCHAR Char;
  894. UINT uOperation = ERK_CREATE;
  895. CurrentIndex = 0;
  896. LastRightBracketIndex = -1;
  897. if (!GetChar(&Char))
  898. return;
  899. if (Char == TEXT('-')) {
  900. if (!GetChar(&Char))
  901. return;
  902. uOperation = ERK_DELETE;
  903. }
  904. do {
  905. if (IsNewLine(Char))
  906. break;
  907. if (Char == TEXT(']'))
  908. LastRightBracketIndex = CurrentIndex;
  909. FullKeyName[CurrentIndex++] = Char;
  910. if (CurrentIndex == SIZE_FULL_KEYNAME) {
  911. do {
  912. if (Char == TEXT(']'))
  913. LastRightBracketIndex = -1;
  914. if (IsNewLine(Char))
  915. break;
  916. } while (GetChar(&Char));
  917. break;
  918. }
  919. } while (GetChar(&Char));
  920. if (LastRightBracketIndex != -1)
  921. {
  922. FullKeyName[LastRightBracketIndex] = 0;
  923. switch (EditRegistryKey(hComputerItem, lphKey, FullKeyName, uOperation))
  924. {
  925. //
  926. // Be afraid of adding code to handle more error cases here.
  927. //
  928. // We broke Picture Publisher 8.0 by adding an ERROR_BADKEY
  929. // case. As part of their setup, they run regedit on a v4
  930. // reg file that has a bad section, which EditRegistryKey
  931. // will fail to parse with ERROR_BADKEY. We need to keep
  932. // chugging along in that case like Win2K did, or else we
  933. // break their setup.
  934. //
  935. case ERROR_CANTOPEN:
  936. g_FileErrorStringID = IDS_IMPFILEERRREGOPEN;
  937. break;
  938. }
  939. }
  940. }
  941. /*******************************************************************************
  942. *
  943. * ParseValuename
  944. *
  945. * DESCRIPTION:
  946. *
  947. * PARAMETERS:
  948. *
  949. *******************************************************************************/
  950. VOID
  951. NEAR PASCAL
  952. ParseValuename(
  953. HKEY hKey
  954. )
  955. {
  956. PORTVALUEPARAM PortValueParam;
  957. PortValueParam.cbData = MAXVALUENAME_LENGTH * sizeof(TCHAR);
  958. PortValueParam.pbData = LocalAlloc(LPTR, PortValueParam.cbData);
  959. if (PortValueParam.pbData)
  960. {
  961. if (ParseString(&PortValueParam))
  962. {
  963. ParseValue(hKey, (PTSTR)PortValueParam.pbData);
  964. }
  965. else
  966. {
  967. SkipPastEndOfLine();
  968. }
  969. LocalFree(PortValueParam.pbData);
  970. }
  971. }
  972. VOID
  973. NEAR PASCAL
  974. ParseValue(
  975. HKEY hKey,
  976. LPCTSTR lpszValueName
  977. )
  978. {
  979. BOOL fSuccess = TRUE;
  980. BOOL fSkipPastLine = FALSE;
  981. DWORD Type;
  982. DWORD cbData = 0;
  983. DWORD cbMaxData = ALLOCATION_INCR;
  984. LPCTSTR lpPrefix;
  985. PBYTE pbValueDataBuffer;
  986. SkipWhitespace();
  987. if (!MatchChar(TEXT('=')))
  988. {
  989. fSuccess = FALSE;
  990. fSkipPastLine = TRUE;
  991. }
  992. else
  993. {
  994. SkipWhitespace();
  995. pbValueDataBuffer = LocalAlloc(LPTR, cbMaxData);
  996. if (!pbValueDataBuffer)
  997. {
  998. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  999. fSuccess = FALSE;
  1000. }
  1001. else
  1002. {
  1003. //
  1004. // REG_SZ.
  1005. //
  1006. // "ValueName" = "string of text"
  1007. //
  1008. if (MatchChar(TEXT('"')))
  1009. {
  1010. // FEATURE: Line continuations for strings?
  1011. PORTVALUEPARAM PortValueParam;
  1012. PortValueParam.pbData = pbValueDataBuffer;
  1013. PortValueParam.cbData = cbMaxData;
  1014. if (!ParseString(&PortValueParam) || !ParseEndOfLine())
  1015. {
  1016. fSuccess = FALSE;
  1017. fSkipPastLine = TRUE;
  1018. }
  1019. // pointer might have been swapped for one with more memory
  1020. pbValueDataBuffer = PortValueParam.pbData;
  1021. cbData = PortValueParam.cbData;
  1022. Type = REG_SZ;
  1023. }
  1024. //
  1025. // REG_DWORD.
  1026. //
  1027. // "ValueName" = dword: 12345678
  1028. //
  1029. else if (MatchChar(s_DwordPrefix[0])) {
  1030. lpPrefix = &s_DwordPrefix[1];
  1031. while (*lpPrefix != 0)
  1032. {
  1033. if (!MatchChar(*lpPrefix++))
  1034. {
  1035. fSuccess = FALSE;
  1036. fSkipPastLine = TRUE;
  1037. }
  1038. }
  1039. if (fSuccess)
  1040. {
  1041. SkipWhitespace();
  1042. if (!ParseHexDword((LPDWORD) pbValueDataBuffer) || !ParseEndOfLine())
  1043. {
  1044. fSuccess = FALSE;
  1045. fSkipPastLine = TRUE;
  1046. }
  1047. Type = REG_DWORD;
  1048. cbData = sizeof(DWORD);
  1049. }
  1050. }
  1051. else if (MatchChar('-'))
  1052. {
  1053. if (!ParseEndOfLine())
  1054. {
  1055. fSuccess = FALSE;
  1056. fSkipPastLine = TRUE;
  1057. }
  1058. else
  1059. {
  1060. RegDeleteValue(hKey, lpszValueName);
  1061. fSuccess = FALSE;
  1062. }
  1063. }
  1064. //
  1065. // REG_BINARY and other.
  1066. //
  1067. // "ValueName" = hex: 00 , 11 , 22
  1068. // "ValueName" = hex(12345678): 00, 11, 22
  1069. //
  1070. else {
  1071. lpPrefix = s_HexPrefix;
  1072. while (*lpPrefix != 0)
  1073. {
  1074. if (!MatchChar(*lpPrefix++))
  1075. {
  1076. fSuccess = FALSE;
  1077. fSkipPastLine = TRUE;
  1078. }
  1079. }
  1080. if (fSuccess)
  1081. {
  1082. //
  1083. // Check if this is a type of registry data that we don't directly
  1084. // support. If so, then it's just a dump of hex data of the specified
  1085. // type.
  1086. //
  1087. if (MatchChar(TEXT('(')))
  1088. {
  1089. if (!ParseHexDword(&Type) || !MatchChar(TEXT(')')))
  1090. {
  1091. fSuccess = FALSE;
  1092. fSkipPastLine = TRUE;
  1093. }
  1094. }
  1095. else
  1096. Type = REG_BINARY;
  1097. if (fSuccess)
  1098. {
  1099. PORTVALUEPARAM PortValueParam;
  1100. PortValueParam.pbData = pbValueDataBuffer;
  1101. PortValueParam.cbData = cbMaxData;
  1102. if (!MatchChar(TEXT(':')) || !ParseHexSequence(&PortValueParam) ||
  1103. !ParseEndOfLine())
  1104. {
  1105. fSuccess = FALSE;
  1106. fSkipPastLine = TRUE;
  1107. }
  1108. // pointer might have been swapped for one with more memory
  1109. pbValueDataBuffer = PortValueParam.pbData;
  1110. cbData = PortValueParam.cbData;
  1111. }
  1112. }
  1113. }
  1114. if (fSuccess)
  1115. {
  1116. #ifdef UNICODE
  1117. //
  1118. // If we're compiled UNICODE and we're reading an older, ANSI .reg
  1119. // file, we have to write all of the data to the registry using
  1120. // RegSetValueExA, because it was read from the registry using
  1121. // RegQueryValueExA.
  1122. //
  1123. if ((g_ImportFileVersion < 0x0500) && ((REG_EXPAND_SZ == Type) || (REG_MULTI_SZ == Type)))
  1124. {
  1125. CHAR AnsiValueName[MAXVALUENAME_LENGTH];
  1126. AnsiValueName[0] = 0;
  1127. //
  1128. // It's much easier to convert the value name to ANSI
  1129. // and call RegSetValueExA than to try to convert
  1130. // a REG_MULTI_SZ to Unicode before calling RegSetValueExW.
  1131. // We don't lose anything because this is coming from a
  1132. // downlevel .reg file that could only contain ANSI characters
  1133. // to begin with.
  1134. //
  1135. WideCharToMultiByte(
  1136. CP_THREAD_ACP,
  1137. 0,
  1138. lpszValueName,
  1139. -1,
  1140. AnsiValueName,
  1141. MAXVALUENAME_LENGTH,
  1142. NULL,
  1143. NULL
  1144. );
  1145. if (RegSetValueExA(
  1146. hKey,
  1147. AnsiValueName,
  1148. 0,
  1149. Type,
  1150. pbValueDataBuffer,
  1151. cbData)
  1152. != ERROR_SUCCESS)
  1153. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  1154. }
  1155. else {
  1156. #endif // UNICODE
  1157. if (RegSetValueEx(hKey, lpszValueName, 0, Type, pbValueDataBuffer, cbData) !=
  1158. ERROR_SUCCESS)
  1159. g_FileErrorStringID = IDS_IMPFILEERRREGSET;
  1160. #ifdef UNICODE
  1161. }
  1162. #endif // UNICODE
  1163. }
  1164. LocalFree(pbValueDataBuffer);
  1165. }
  1166. }
  1167. if (fSkipPastLine)
  1168. {
  1169. SkipPastEndOfLine();
  1170. }
  1171. }
  1172. /*******************************************************************************
  1173. *
  1174. * ParseString
  1175. *
  1176. * DESCRIPTION:
  1177. *
  1178. * PARAMETERS:
  1179. *
  1180. *******************************************************************************/
  1181. BOOL
  1182. NEAR PASCAL
  1183. ParseString(LPPORTVALUEPARAM pPortValueParam)
  1184. {
  1185. TCHAR Char;
  1186. DWORD cbMaxStringData;
  1187. DWORD cbStringData;
  1188. LPTSTR psz = (LPTSTR)pPortValueParam->pbData; // this one is incremented
  1189. cbMaxStringData = pPortValueParam->cbData;
  1190. cbStringData = sizeof(TCHAR); // Account for the null terminator
  1191. while (GetChar(&Char))
  1192. {
  1193. if (cbStringData >= cbMaxStringData)
  1194. {
  1195. // allocate a bigger buffer
  1196. PBYTE pbValueData =
  1197. LocalReAlloc(pPortValueParam->pbData, cbMaxStringData + ALLOCATION_INCR, LMEM_MOVEABLE);
  1198. if (pbValueData)
  1199. {
  1200. pPortValueParam->pbData = pbValueData;
  1201. // incr psz to next char in new buffer
  1202. psz = (LPTSTR)(pPortValueParam->pbData + (cbMaxStringData - sizeof(TCHAR)));
  1203. cbMaxStringData += ALLOCATION_INCR;
  1204. }
  1205. else
  1206. {
  1207. break;
  1208. }
  1209. }
  1210. switch (Char) {
  1211. case TEXT('\\'):
  1212. if (!GetChar(&Char))
  1213. return FALSE;
  1214. switch (Char) {
  1215. case TEXT('\\'):
  1216. *psz++ = TEXT('\\');
  1217. break;
  1218. case TEXT('"'):
  1219. *psz++ = TEXT('"');
  1220. break;
  1221. case TEXT('r'):
  1222. if(g_fMultiLineStrings)
  1223. {
  1224. *psz++ = TEXT('\r');
  1225. break;
  1226. }
  1227. else
  1228. {
  1229. DebugPrintf(("ParseString: Invalid escape sequence"));
  1230. return FALSE;
  1231. }
  1232. case TEXT('n'):
  1233. if(g_fMultiLineStrings)
  1234. {
  1235. *psz++ = TEXT('\n');
  1236. break;
  1237. }
  1238. else
  1239. {
  1240. DebugPrintf(("ParseString: Invalid escape sequence"));
  1241. return FALSE;
  1242. }
  1243. default:
  1244. DebugPrintf(("ParseString: Invalid escape sequence"));
  1245. return FALSE;
  1246. }
  1247. break;
  1248. case TEXT('"'):
  1249. *psz = 0;
  1250. pPortValueParam->cbData = cbStringData;
  1251. return TRUE;
  1252. default:
  1253. if (IsNewLine(Char))
  1254. return FALSE;
  1255. *psz++ = Char;
  1256. break;
  1257. }
  1258. cbStringData += sizeof(TCHAR);
  1259. }
  1260. return FALSE;
  1261. }
  1262. /*******************************************************************************
  1263. *
  1264. * ParseHexSequence
  1265. *
  1266. * DESCRIPTION:
  1267. *
  1268. * PARAMETERS:
  1269. *
  1270. *******************************************************************************/
  1271. BOOL
  1272. NEAR PASCAL
  1273. ParseHexSequence(LPPORTVALUEPARAM pPortValueParam)
  1274. {
  1275. BOOL fSuccess = TRUE;
  1276. DWORD cbHexData = 0;
  1277. DWORD cbMaxStringData = pPortValueParam->cbData;
  1278. LPBYTE lpHexData = pPortValueParam->pbData;
  1279. do
  1280. {
  1281. if (cbHexData >= cbMaxStringData)
  1282. {
  1283. // allocate a bigger buffer
  1284. PBYTE pbValueData = LocalReAlloc(pPortValueParam->pbData,
  1285. cbMaxStringData + ALLOCATION_INCR, LMEM_MOVEABLE);
  1286. if (pbValueData)
  1287. {
  1288. pPortValueParam->pbData = pbValueData;
  1289. // incr psz to next char in new buffer
  1290. lpHexData = pPortValueParam->pbData + cbMaxStringData;
  1291. cbMaxStringData += ALLOCATION_INCR;
  1292. }
  1293. else
  1294. {
  1295. fSuccess = FALSE;
  1296. break;
  1297. }
  1298. }
  1299. SkipWhitespace();
  1300. if (MatchChar(TEXT('\\')) && !ParseEndOfLine())
  1301. {
  1302. fSuccess = FALSE;
  1303. break;
  1304. }
  1305. SkipWhitespace();
  1306. if (!ParseHexByte(lpHexData++))
  1307. break;
  1308. cbHexData++;
  1309. SkipWhitespace();
  1310. } while (MatchChar(TEXT(',')));
  1311. pPortValueParam->cbData = cbHexData;
  1312. return fSuccess;
  1313. }
  1314. /*******************************************************************************
  1315. *
  1316. * ParseHexDword
  1317. *
  1318. * DESCRIPTION:
  1319. * Parses a one dword hexadecimal string from the registry file stream and
  1320. * converts it to a binary number. A maximum of eight hex digits will be
  1321. * parsed from the stream.
  1322. *
  1323. * PARAMETERS:
  1324. * lpByte, location to store binary number.
  1325. * (returns), TRUE if a hexadecimal dword was parsed, else FALSE.
  1326. *
  1327. *******************************************************************************/
  1328. BOOL
  1329. NEAR PASCAL
  1330. ParseHexDword(
  1331. LPDWORD lpDword
  1332. )
  1333. {
  1334. UINT CountDigits;
  1335. DWORD Dword;
  1336. BYTE Byte;
  1337. Dword = 0;
  1338. CountDigits = 0;
  1339. while (TRUE) {
  1340. if (!ParseHexDigit(&Byte))
  1341. break;
  1342. Dword = (Dword << 4) + (DWORD) Byte;
  1343. if (++CountDigits == 8)
  1344. break;
  1345. }
  1346. *lpDword = Dword;
  1347. return CountDigits != 0;
  1348. }
  1349. /*******************************************************************************
  1350. *
  1351. * ParseHexByte
  1352. *
  1353. * DESCRIPTION:
  1354. * Parses a one byte hexadecimal string from the registry file stream and
  1355. * converts it to a binary number.
  1356. *
  1357. * PARAMETERS:
  1358. * lpByte, location to store binary number.
  1359. * (returns), TRUE if a hexadecimal byte was parsed, else FALSE.
  1360. *
  1361. *******************************************************************************/
  1362. BOOL
  1363. NEAR PASCAL
  1364. ParseHexByte(
  1365. LPBYTE lpByte
  1366. )
  1367. {
  1368. BYTE SecondDigit;
  1369. if (ParseHexDigit(lpByte)) {
  1370. if (ParseHexDigit(&SecondDigit))
  1371. *lpByte = (BYTE) ((*lpByte << 4) | SecondDigit);
  1372. return TRUE;
  1373. }
  1374. else
  1375. return FALSE;
  1376. }
  1377. /*******************************************************************************
  1378. *
  1379. * ParseHexDigit
  1380. *
  1381. * DESCRIPTION:
  1382. * Parses a hexadecimal character from the registry file stream and converts
  1383. * it to a binary number.
  1384. *
  1385. * PARAMETERS:
  1386. * lpDigit, location to store binary number.
  1387. * (returns), TRUE if a hexadecimal digit was parsed, else FALSE.
  1388. *
  1389. *******************************************************************************/
  1390. BOOL
  1391. NEAR PASCAL
  1392. ParseHexDigit(
  1393. LPBYTE lpDigit
  1394. )
  1395. {
  1396. TCHAR Char;
  1397. BYTE Digit;
  1398. if (GetChar(&Char)) {
  1399. if (Char >= TEXT('0') && Char <= TEXT('9'))
  1400. Digit = (BYTE) (Char - TEXT('0'));
  1401. else if (Char >= TEXT('a') && Char <= TEXT('f'))
  1402. Digit = (BYTE) (Char - TEXT('a') + 10);
  1403. else if (Char >= TEXT('A') && Char <= TEXT('F'))
  1404. Digit = (BYTE) (Char - TEXT('A') + 10);
  1405. else {
  1406. UngetChar();
  1407. return FALSE;
  1408. }
  1409. *lpDigit = Digit;
  1410. return TRUE;
  1411. }
  1412. return FALSE;
  1413. }
  1414. /*******************************************************************************
  1415. *
  1416. * ParseEndOfLine
  1417. *
  1418. * DESCRIPTION:
  1419. *
  1420. * PARAMETERS:
  1421. *
  1422. *******************************************************************************/
  1423. BOOL
  1424. NEAR PASCAL
  1425. ParseEndOfLine(
  1426. VOID
  1427. )
  1428. {
  1429. TCHAR Char;
  1430. BOOL fComment;
  1431. BOOL fFoundOneEndOfLine;
  1432. BOOL fEOF;
  1433. fComment = FALSE;
  1434. fFoundOneEndOfLine = FALSE;
  1435. fEOF = TRUE;
  1436. while (GetChar(&Char)) {
  1437. if (IsWhitespace(Char))
  1438. continue;
  1439. if (IsNewLine(Char)) {
  1440. fComment = FALSE;
  1441. fFoundOneEndOfLine = TRUE;
  1442. }
  1443. //
  1444. // Like .INIs and .INFs, comments begin with a semicolon character.
  1445. //
  1446. else if (Char == TEXT(';'))
  1447. fComment = TRUE;
  1448. else if (!fComment) {
  1449. UngetChar();
  1450. fEOF = FALSE;
  1451. break;
  1452. }
  1453. }
  1454. return fFoundOneEndOfLine || fEOF;
  1455. }
  1456. /*******************************************************************************
  1457. *
  1458. * SkipWhitespace
  1459. *
  1460. * DESCRIPTION:
  1461. * Advances the registry file pointer to the first character past any
  1462. * detected whitespace.
  1463. *
  1464. * PARAMETERS:
  1465. * (none).
  1466. *
  1467. *******************************************************************************/
  1468. VOID
  1469. NEAR PASCAL
  1470. SkipWhitespace(
  1471. VOID
  1472. )
  1473. {
  1474. TCHAR Char;
  1475. while (GetChar(&Char)) {
  1476. if (!IsWhitespace(Char)) {
  1477. UngetChar();
  1478. break;
  1479. }
  1480. }
  1481. }
  1482. /*******************************************************************************
  1483. *
  1484. * SkipPastEndOfLine
  1485. *
  1486. * DESCRIPTION:
  1487. * Advances the registry file pointer to the first character past the first
  1488. * detected new line character.
  1489. *
  1490. * PARAMETERS:
  1491. * (none).
  1492. *
  1493. *******************************************************************************/
  1494. VOID
  1495. NEAR PASCAL
  1496. SkipPastEndOfLine(
  1497. VOID
  1498. )
  1499. {
  1500. TCHAR Char;
  1501. while (GetChar(&Char)) {
  1502. if (IsNewLine(Char))
  1503. break;
  1504. }
  1505. while (GetChar(&Char)) {
  1506. if (!IsNewLine(Char)) {
  1507. UngetChar();
  1508. break;
  1509. }
  1510. }
  1511. }
  1512. /*******************************************************************************
  1513. *
  1514. * GetChar
  1515. *
  1516. * DESCRIPTION:
  1517. *
  1518. * PARAMETERS:
  1519. *
  1520. *******************************************************************************/
  1521. BOOL
  1522. NEAR PASCAL
  1523. GetChar(
  1524. PTCHAR lpChar
  1525. )
  1526. {
  1527. FILE_NUMBYTES NumberOfBytesRead;
  1528. UINT NewPercentage;
  1529. // If we're at the end of the buffer, read some more.
  1530. // Initially BufferOffset and CharsAvailable will be 0
  1531. if (s_FileIo.BufferOffset == s_FileIo.CharsAvailable) {
  1532. if (TRUE == s_fTreatFileAsUnicode) {
  1533. if (!READFILE(s_FileIo.hFile, s_FileIo.Buffer,
  1534. SIZE_FILE_IO_BUFFER, &NumberOfBytesRead)) {
  1535. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  1536. return FALSE;
  1537. }
  1538. s_FileIo.CharsAvailable = ((int) NumberOfBytesRead / 2);
  1539. }
  1540. else {
  1541. if (!READFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer,
  1542. SIZE_FILE_IO_BUFFER, &NumberOfBytesRead)) {
  1543. g_FileErrorStringID = IDS_IMPFILEERRFILEREAD;
  1544. return FALSE;
  1545. }
  1546. {
  1547. int i;
  1548. i = MultiByteToWideChar(
  1549. CP_THREAD_ACP,
  1550. MB_PRECOMPOSED,
  1551. s_FileIo.ConversionBuffer,
  1552. NumberOfBytesRead,
  1553. s_FileIo.Buffer,
  1554. SIZE_FILE_IO_BUFFER
  1555. );
  1556. s_FileIo.CharsAvailable = i;
  1557. }
  1558. }
  1559. s_FileIo.BufferOffset = 0;
  1560. s_FileIo.FileOffset += NumberOfBytesRead;
  1561. if (s_FileIo.FileSizeDiv100 != 0) {
  1562. NewPercentage = ((UINT) (s_FileIo.FileOffset /
  1563. s_FileIo.FileSizeDiv100));
  1564. if (NewPercentage > 100)
  1565. NewPercentage = 100;
  1566. }
  1567. else
  1568. NewPercentage = 100;
  1569. if (s_FileIo.LastPercentage != NewPercentage) {
  1570. s_FileIo.LastPercentage = NewPercentage;
  1571. ImportRegFileUICallback(NewPercentage);
  1572. }
  1573. }
  1574. if (s_FileIo.BufferOffset >= s_FileIo.CharsAvailable)
  1575. return FALSE;
  1576. *lpChar = s_FileIo.Buffer[s_FileIo.BufferOffset++];
  1577. return TRUE;
  1578. }
  1579. /*******************************************************************************
  1580. *
  1581. * UngetChar
  1582. *
  1583. * DESCRIPTION:
  1584. *
  1585. * PARAMETERS:
  1586. *
  1587. *******************************************************************************/
  1588. VOID
  1589. NEAR PASCAL
  1590. UngetChar(
  1591. VOID
  1592. )
  1593. {
  1594. #ifdef DEBUG
  1595. if (s_FileIo.fValidateUngetChar)
  1596. DebugPrintf(("REGEDIT ERROR: Too many UngetChar's called!\n\r"));
  1597. #endif
  1598. s_FileIo.BufferOffset--;
  1599. }
  1600. /*******************************************************************************
  1601. *
  1602. * MatchChar
  1603. *
  1604. * DESCRIPTION:
  1605. *
  1606. * PARAMETERS:
  1607. *
  1608. *******************************************************************************/
  1609. BOOL
  1610. NEAR PASCAL
  1611. MatchChar(
  1612. TCHAR CharToMatch
  1613. )
  1614. {
  1615. BOOL fMatch;
  1616. TCHAR NextChar;
  1617. fMatch = FALSE;
  1618. if (GetChar(&NextChar)) {
  1619. if (CharToMatch == NextChar)
  1620. fMatch = TRUE;
  1621. else
  1622. UngetChar();
  1623. }
  1624. return fMatch;
  1625. }
  1626. /*******************************************************************************
  1627. *
  1628. * IsWhitespace
  1629. *
  1630. * DESCRIPTION:
  1631. * Checks if the given character is whitespace.
  1632. *
  1633. * PARAMETERS:
  1634. * Char, character to check.
  1635. * (returns), TRUE if character is whitespace, else FALSE.
  1636. *
  1637. *******************************************************************************/
  1638. BOOL
  1639. NEAR PASCAL
  1640. IsWhitespace(
  1641. TCHAR Char
  1642. )
  1643. {
  1644. return Char == TEXT(' ') || Char == TEXT('\t');
  1645. }
  1646. /*******************************************************************************
  1647. *
  1648. * IsNewLine
  1649. *
  1650. * DESCRIPTION:
  1651. * Checks if the given character is a new line character.
  1652. *
  1653. * PARAMETERS:
  1654. * Char, character to check.
  1655. * (returns), TRUE if character is a new line, else FALSE.
  1656. *
  1657. *******************************************************************************/
  1658. BOOL
  1659. NEAR PASCAL
  1660. IsNewLine(
  1661. TCHAR Char
  1662. )
  1663. {
  1664. return Char == TEXT('\n') || Char == TEXT('\r');
  1665. }
  1666. /*******************************************************************************
  1667. *
  1668. * ExportWinNT50RegFile
  1669. *
  1670. * DESCRIPTION:
  1671. * Exports an NT 5.0, unicode registry file. Use this export function
  1672. * for all future .reg file writing.
  1673. *
  1674. * PARAMETERS:
  1675. *
  1676. *******************************************************************************/
  1677. VOID ExportWinNT50RegFile(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1678. {
  1679. HKEY hKey;
  1680. TCHAR SelectedPath[SIZE_SELECTED_PATH];
  1681. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1682. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1683. if (lpSelectedPath != NULL && EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
  1684. &hKey, lpSelectedPath, ERK_OPEN) != ERROR_SUCCESS)
  1685. {
  1686. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1687. return;
  1688. }
  1689. if (OPENWRITEFILE(lpFileName, s_FileIo.hFile)) {
  1690. DWORD dwNumberOfBytesWritten;
  1691. s_FileIo.BufferOffset = 0;
  1692. s_FileIo.CurrentColumn = 0;
  1693. WRITEFILE(s_FileIo.hFile, &s_UnicodeByteOrderMark, sizeof(s_UnicodeByteOrderMark), &dwNumberOfBytesWritten);
  1694. PutLiteral(s_WinNT50RegFileHeader);
  1695. PutLiteral(TEXT(" "));
  1696. PutLiteral(s_WinNT50RegFileVersion);
  1697. PutLiteral(TEXT("\n\n"));
  1698. if (lpSelectedPath != NULL) {
  1699. STRCPY(SelectedPath, lpSelectedPath);
  1700. PutBranch(hKey, SelectedPath);
  1701. }
  1702. else
  1703. {
  1704. HTREEITEM hComputerItem = RegEdit_GetComputerItem(hSelectedTreeItem);
  1705. STRCPY(SelectedPath,
  1706. g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
  1707. PutBranch(Regedit_GetRootKeyFromComputer(hComputerItem, SelectedPath), SelectedPath);
  1708. STRCPY(SelectedPath,
  1709. g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
  1710. PutBranch(Regedit_GetRootKeyFromComputer(hComputerItem, SelectedPath), SelectedPath);
  1711. }
  1712. FlushIoBuffer();
  1713. CLOSEFILE(s_FileIo.hFile);
  1714. }
  1715. else
  1716. g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
  1717. if (lpSelectedPath != NULL)
  1718. RegCloseKey(hKey);
  1719. }
  1720. //------------------------------------------------------------------------------
  1721. // ExportRegedt32File
  1722. //
  1723. // DESCRIPTION: This function exports file in the format of Regedt32.
  1724. // The binary is key is stored to a file without any key path info
  1725. //
  1726. // PARAMETERS: LPTSTR lpFileName
  1727. // LPTSTR lpSelectedPath
  1728. //------------------------------------------------------------------------------
  1729. void ExportRegedt32File(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1730. {
  1731. HKEY hKey = NULL;
  1732. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1733. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1734. if (lpSelectedPath == NULL || EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
  1735. &hKey, lpSelectedPath, ERK_OPEN) == ERROR_SUCCESS)
  1736. {
  1737. HRESULT hr;
  1738. RegEdit_SetPrivilege(SE_BACKUP_NAME, TRUE);
  1739. //
  1740. // If a file with the same name already exists, we need to delete
  1741. // it ourselves before calling RegSaveKey or that call will fail.
  1742. //
  1743. DeleteFile(lpFileName);
  1744. if ((hr = RegSaveKey(hKey, lpFileName, NULL)) != ERROR_SUCCESS)
  1745. {
  1746. switch (hr)
  1747. {
  1748. case ERROR_PRIVILEGE_NOT_HELD:
  1749. g_FileErrorStringID = IDS_EXPFILEERRNOPRIV;
  1750. break;
  1751. case ERROR_INVALID_HANDLE:
  1752. g_FileErrorStringID = IDS_EXPFILEERRINVALID;
  1753. break;
  1754. default:
  1755. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1756. break;
  1757. }
  1758. // RegSaveKey created an empty file
  1759. DeleteFile(lpFileName);
  1760. }
  1761. RegEdit_SetPrivilege(SE_BACKUP_NAME, FALSE);
  1762. }
  1763. else
  1764. {
  1765. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1766. }
  1767. }
  1768. /*******************************************************************************
  1769. *
  1770. * ExportWin40RegFile
  1771. *
  1772. * DESCRIPTION:
  1773. * This function is only kept around to export old, ANSI, regedit 4 .reg
  1774. * files. Don't touch it except to fix bugs. Meddling with this code
  1775. * path will result in .reg files that can't be read by older verions
  1776. * of regedit, which is the whole reason this code path is here. Meddle
  1777. * with ExportWinNT50RegFile if you want to break backwards compatibility.
  1778. *
  1779. * PARAMETERS:
  1780. *
  1781. *******************************************************************************/
  1782. VOID ExportWin40RegFile(LPTSTR lpFileName, LPTSTR lpSelectedPath)
  1783. {
  1784. HKEY hKey;
  1785. TCHAR SelectedPath[SIZE_SELECTED_PATH];
  1786. HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);
  1787. g_FileErrorStringID = IDS_EXPFILEERRSUCCESS;
  1788. if (lpSelectedPath != NULL && EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
  1789. &hKey, lpSelectedPath, ERK_OPEN) != ERROR_SUCCESS)
  1790. {
  1791. g_FileErrorStringID = IDS_EXPFILEERRBADREGPATH;
  1792. return;
  1793. }
  1794. if (OPENWRITEFILE(lpFileName, s_FileIo.hFile))
  1795. {
  1796. s_FileIo.BufferOffset = 0;
  1797. s_FileIo.CurrentColumn = 0;
  1798. PutLiteral(s_Win40RegFileHeader);
  1799. if (lpSelectedPath != NULL) {
  1800. STRCPY(SelectedPath, lpSelectedPath);
  1801. PutBranch(hKey, SelectedPath);
  1802. }
  1803. else {
  1804. STRCPY(SelectedPath,
  1805. g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
  1806. PutBranch(HKEY_LOCAL_MACHINE, SelectedPath);
  1807. STRCPY(SelectedPath,
  1808. g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
  1809. PutBranch(HKEY_USERS, SelectedPath);
  1810. }
  1811. FlushIoBuffer();
  1812. CLOSEFILE(s_FileIo.hFile);
  1813. }
  1814. else
  1815. g_FileErrorStringID = IDS_EXPFILEERRFILEOPEN;
  1816. if (lpSelectedPath != NULL)
  1817. RegCloseKey(hKey);
  1818. }
  1819. /*******************************************************************************
  1820. *
  1821. * PutBranch
  1822. *
  1823. * DESCRIPTION:
  1824. * Writes out all of the value names and their data and recursively calls
  1825. * this routine for all of the key's subkeys to the registry file stream.
  1826. *
  1827. * PARAMETERS:
  1828. * hKey, registry key to write to file.
  1829. * lpFullKeyName, string that gives the full path, including the root key
  1830. * name, of the hKey.
  1831. *
  1832. *******************************************************************************/
  1833. VOID
  1834. NEAR PASCAL
  1835. PutBranch(
  1836. HKEY hKey,
  1837. LPTSTR lpFullKeyName
  1838. )
  1839. {
  1840. LONG RegError;
  1841. DWORD EnumIndex;
  1842. DWORD cchValueName;
  1843. DWORD cbValueData;
  1844. DWORD Type;
  1845. LPTSTR lpSubKeyName;
  1846. HKEY hSubKey;
  1847. int nLenFullKey;
  1848. LPTSTR lpTempFullKeyName;
  1849. //
  1850. // Write out the section header.
  1851. //
  1852. PutChar(TEXT('['));
  1853. PutLiteral(lpFullKeyName);
  1854. PutLiteral(TEXT("]\n"));
  1855. //
  1856. // Write out all of the value names and their data.
  1857. //
  1858. EnumIndex = 0;
  1859. while (TRUE)
  1860. {
  1861. PBYTE pbValueData;
  1862. cchValueName = ARRAYSIZE(g_ValueNameBuffer);
  1863. // VALUE DATA
  1864. // Query for data size
  1865. RegError = RegEnumValue(hKey, EnumIndex++, g_ValueNameBuffer,
  1866. &cchValueName, NULL, &Type, NULL, &cbValueData);
  1867. if (RegError != ERROR_SUCCESS)
  1868. {
  1869. break;
  1870. }
  1871. // allocate memory for data
  1872. pbValueData = LocalAlloc(LPTR, cbValueData+ExtraAllocLen(Type));
  1873. if (pbValueData)
  1874. {
  1875. if (RegEdit_QueryValueEx(hKey, g_ValueNameBuffer,
  1876. NULL, &Type, pbValueData, &cbValueData) !=
  1877. ERROR_SUCCESS)
  1878. {
  1879. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1880. }
  1881. else
  1882. {
  1883. //
  1884. // If cbValueName is zero, then this is the default value of
  1885. // the key, or the Windows 3.1 compatible key value.
  1886. //
  1887. if (cchValueName)
  1888. PutString(g_ValueNameBuffer);
  1889. else
  1890. PutChar(TEXT('@'));
  1891. PutChar(TEXT('='));
  1892. switch (Type)
  1893. {
  1894. case REG_SZ:
  1895. PutString((LPTSTR) pbValueData);
  1896. break;
  1897. case REG_DWORD:
  1898. if (cbValueData == sizeof(DWORD))
  1899. {
  1900. PutLiteral(s_DwordPrefix);
  1901. PutDword(*((LPDWORD) pbValueData), TRUE);
  1902. break;
  1903. }
  1904. // FALL THROUGH
  1905. case REG_BINARY:
  1906. default:
  1907. PutBinary((LPBYTE) pbValueData, Type, cbValueData);
  1908. break;
  1909. }
  1910. PutChar(TEXT('\n'));
  1911. }
  1912. LocalFree(pbValueData);
  1913. }
  1914. else
  1915. {
  1916. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  1917. }
  1918. if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
  1919. return;
  1920. }
  1921. PutChar(TEXT('\n'));
  1922. if (RegError != ERROR_NO_MORE_ITEMS)
  1923. g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
  1924. //
  1925. // Write out all of the subkeys and recurse into them.
  1926. //
  1927. //copy the existing key into a new buffer with enough room for the next key
  1928. nLenFullKey = lstrlen(lpFullKeyName);
  1929. lpTempFullKeyName = (LPTSTR) alloca( (nLenFullKey+MAXKEYNAME)*sizeof(TCHAR));
  1930. lstrcpy(lpTempFullKeyName, lpFullKeyName);
  1931. lpSubKeyName = lpTempFullKeyName + nLenFullKey;
  1932. *lpSubKeyName++ = TEXT('\\');
  1933. *lpSubKeyName = 0;
  1934. EnumIndex = 0;
  1935. while (TRUE) {
  1936. if ((RegError = RegEnumKey(hKey, EnumIndex++, lpSubKeyName, MAXKEYNAME-1)) != ERROR_SUCCESS)
  1937. break;
  1938. if(RegOpenKeyEx(hKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSubKey) == ERROR_SUCCESS) {
  1939. PutBranch(hSubKey, lpTempFullKeyName);
  1940. RegCloseKey(hSubKey);
  1941. if (g_FileErrorStringID == IDS_EXPFILEERRFILEWRITE)
  1942. return;
  1943. }
  1944. else
  1945. g_FileErrorStringID = IDS_EXPFILEERRREGOPEN;
  1946. }
  1947. if (RegError != ERROR_NO_MORE_ITEMS)
  1948. g_FileErrorStringID = IDS_EXPFILEERRREGENUM;
  1949. }
  1950. /*******************************************************************************
  1951. *
  1952. * PutLiteral
  1953. *
  1954. * DESCRIPTION:
  1955. * Writes a literal string to the registry file stream. No special handling
  1956. * is done for the string-- it is written out as is.
  1957. *
  1958. * PARAMETERS:
  1959. * lpLiteral, null-terminated literal to write to file.
  1960. *
  1961. *******************************************************************************/
  1962. VOID
  1963. NEAR PASCAL
  1964. PutLiteral(
  1965. LPCTSTR lpLiteral
  1966. )
  1967. {
  1968. while (*lpLiteral != 0)
  1969. PutChar(*lpLiteral++);
  1970. }
  1971. /*******************************************************************************
  1972. *
  1973. * PutString
  1974. *
  1975. * DESCRIPTION:
  1976. * Writes a string to the registry file stream. A string is surrounded by
  1977. * double quotes and some characters may be translated to escape sequences
  1978. * to enable a parser to read the string back in.
  1979. *
  1980. * PARAMETERS:
  1981. * lpString, null-terminated string to write to file.
  1982. *
  1983. *******************************************************************************/
  1984. VOID
  1985. NEAR PASCAL
  1986. PutString(
  1987. LPCTSTR lpString
  1988. )
  1989. {
  1990. TCHAR Char;
  1991. PutChar(TEXT('"'));
  1992. while ((Char = *lpString++) != 0) {
  1993. switch (Char) {
  1994. case TEXT('\r'):
  1995. if(g_fMultiLineStrings)
  1996. {
  1997. PutChar('\\');
  1998. PutChar('r');
  1999. }
  2000. else
  2001. {
  2002. PutChar(Char);
  2003. }
  2004. break;
  2005. case TEXT('\n'):
  2006. if(g_fMultiLineStrings)
  2007. {
  2008. PutChar('\\');
  2009. PutChar('n');
  2010. }
  2011. else
  2012. {
  2013. PutChar(Char);
  2014. }
  2015. break;
  2016. case TEXT('\\'):
  2017. case TEXT('"'):
  2018. PutChar(TEXT('\\'));
  2019. // FALL THROUGH
  2020. default:
  2021. PutChar(Char);
  2022. break;
  2023. }
  2024. }
  2025. PutChar(TEXT('"'));
  2026. }
  2027. /*******************************************************************************
  2028. *
  2029. * PutBinary
  2030. *
  2031. * DESCRIPTION:
  2032. * Writes a sequence of hexadecimal bytes to the registry file stream. The
  2033. * output is formatted such that it doesn't exceed a defined line length.
  2034. *
  2035. * PARAMETERS:
  2036. * lpBuffer, bytes to write to file.
  2037. * Type, value data type.
  2038. * cbBytes, number of bytes to write.
  2039. *
  2040. *******************************************************************************/
  2041. VOID
  2042. NEAR PASCAL
  2043. PutBinary(
  2044. CONST BYTE FAR* lpBuffer,
  2045. DWORD Type,
  2046. DWORD cbBytes
  2047. )
  2048. {
  2049. BOOL fFirstByteOnLine;
  2050. BYTE Byte;
  2051. // If we're writing one of the string formats that regedit doesn't write
  2052. // natively (but rather converts to a string of hex digits for streaming
  2053. // out), AND we're writing in downlevel/ANSI/REGEDIT4 format, we aren't
  2054. // going to write out the high byte of each (internally Unicode) character.
  2055. // So we will be writing half as many characters as the buffer byte size.
  2056. if ((g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4) &&
  2057. ((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
  2058. cbBytes = cbBytes / 2;
  2059. }
  2060. PutLiteral(s_HexPrefix);
  2061. if (Type != REG_BINARY) {
  2062. PutChar(TEXT('('));
  2063. PutDword(Type, FALSE);
  2064. PutChar(TEXT(')'));
  2065. }
  2066. PutChar(TEXT(':'));
  2067. fFirstByteOnLine = TRUE;
  2068. while (cbBytes--) {
  2069. if (s_FileIo.CurrentColumn > 75 && !fFirstByteOnLine) {
  2070. PutLiteral(s_FileLineBreak);
  2071. fFirstByteOnLine = TRUE;
  2072. }
  2073. if (!fFirstByteOnLine)
  2074. PutChar(TEXT(','));
  2075. Byte = *lpBuffer++;
  2076. // If we're writing one of the string formats that regedit doesn't
  2077. // write natively (REG_EXPAND_SZ and REG_MULTI_SZ values get converted
  2078. // to a string of hex digits for streaming out), AND we're writing in
  2079. // downlevel/ANSI/REGEDIT4 format, we don't want to write out the high
  2080. // byte of each (internally Unicode) character. So in those cases, we
  2081. // advance another byte to get to the next ANSI character. Yes, this
  2082. // will lose data on non-SBCS characters, but that's what you get for
  2083. // saving in the downlevel format.
  2084. if ((g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4) &&
  2085. ((Type == REG_EXPAND_SZ) || (Type == REG_MULTI_SZ))) {
  2086. lpBuffer++;
  2087. }
  2088. PutChar(g_HexConversion[Byte >> 4]);
  2089. PutChar(g_HexConversion[Byte & 0x0F]);
  2090. fFirstByteOnLine = FALSE;
  2091. }
  2092. }
  2093. /*******************************************************************************
  2094. *
  2095. * PutChar
  2096. *
  2097. * DESCRIPTION:
  2098. * Writes a 32-bit word to the registry file stream.
  2099. *
  2100. * PARAMETERS:
  2101. * Dword, dword to write to file.
  2102. *
  2103. *******************************************************************************/
  2104. VOID
  2105. NEAR PASCAL
  2106. PutDword(
  2107. DWORD Dword,
  2108. BOOL fLeadingZeroes
  2109. )
  2110. {
  2111. int CurrentNibble;
  2112. TCHAR Char;
  2113. BOOL fWroteNonleadingChar;
  2114. fWroteNonleadingChar = fLeadingZeroes;
  2115. for (CurrentNibble = 7; CurrentNibble >= 0; CurrentNibble--) {
  2116. Char = g_HexConversion[(Dword >> (CurrentNibble * 4)) & 0x0F];
  2117. if (fWroteNonleadingChar || Char != TEXT('0')) {
  2118. PutChar(Char);
  2119. fWroteNonleadingChar = TRUE;
  2120. }
  2121. }
  2122. //
  2123. // We need to write at least one character, so if we haven't written
  2124. // anything yet, just spit out one zero.
  2125. //
  2126. if (!fWroteNonleadingChar)
  2127. PutChar(TEXT('0'));
  2128. }
  2129. /*******************************************************************************
  2130. *
  2131. * PutChar
  2132. *
  2133. * DESCRIPTION:
  2134. * Writes one character to the registry file stream using an intermediate
  2135. * buffer.
  2136. *
  2137. * PARAMETERS:
  2138. * Char, character to write to file.
  2139. *
  2140. *******************************************************************************/
  2141. VOID
  2142. NEAR PASCAL
  2143. PutChar(
  2144. TCHAR Char
  2145. )
  2146. {
  2147. //
  2148. // Keep track of what column we're currently at. This is useful in cases
  2149. // such as writing a large binary registry record. Instead of writing one
  2150. // very long line, the other Put* routines can break up their output.
  2151. //
  2152. if (Char != TEXT('\n'))
  2153. s_FileIo.CurrentColumn++;
  2154. else {
  2155. //
  2156. // Force a carriage-return, line-feed sequence to keep things like, oh,
  2157. // Notepad happy.
  2158. //
  2159. PutChar(TEXT('\r'));
  2160. s_FileIo.CurrentColumn = 0;
  2161. }
  2162. s_FileIo.Buffer[s_FileIo.BufferOffset++] = Char;
  2163. if (s_FileIo.BufferOffset == SIZE_FILE_IO_BUFFER)
  2164. FlushIoBuffer();
  2165. }
  2166. /*******************************************************************************
  2167. *
  2168. * FlushIoBuffer
  2169. *
  2170. * DESCRIPTION:
  2171. * Flushes the contents of the registry file stream to the disk and resets
  2172. * the buffer pointer.
  2173. *
  2174. * PARAMETERS:
  2175. * (none).
  2176. *
  2177. *******************************************************************************/
  2178. VOID
  2179. NEAR PASCAL
  2180. FlushIoBuffer(
  2181. VOID
  2182. )
  2183. {
  2184. FILE_NUMBYTES NumberOfBytesWritten;
  2185. if (s_FileIo.BufferOffset) {
  2186. if (g_RegEditData.uExportFormat == FILE_TYPE_REGEDIT4)
  2187. {
  2188. //
  2189. // Convert Unicode to ANSI before writing.
  2190. //
  2191. int i;
  2192. i = WideCharToMultiByte(
  2193. CP_THREAD_ACP,
  2194. 0,
  2195. s_FileIo.Buffer,
  2196. s_FileIo.BufferOffset,
  2197. s_FileIo.ConversionBuffer,
  2198. sizeof(s_FileIo.ConversionBuffer),
  2199. NULL,
  2200. NULL
  2201. );
  2202. if (!WRITEFILE(s_FileIo.hFile, s_FileIo.ConversionBuffer, i,
  2203. &NumberOfBytesWritten) || (FILE_NUMBYTES) i !=
  2204. NumberOfBytesWritten)
  2205. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  2206. }
  2207. else
  2208. {
  2209. //
  2210. // Write Unicode text
  2211. //
  2212. if (!WRITEFILE(s_FileIo.hFile, s_FileIo.Buffer, s_FileIo.BufferOffset * sizeof(WCHAR),
  2213. &NumberOfBytesWritten) || (FILE_NUMBYTES) (s_FileIo.BufferOffset * sizeof(WCHAR)) !=
  2214. NumberOfBytesWritten)
  2215. g_FileErrorStringID = IDS_EXPFILEERRFILEWRITE;
  2216. }
  2217. }
  2218. s_FileIo.BufferOffset = 0;
  2219. }