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.

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