Leaked source code of windows server 2003
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.

2715 lines
68 KiB

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