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.

705 lines
29 KiB

  1. #include "regutil.h"
  2. BOOLEAN BackwardsCompatibleInput;
  3. typedef struct _KEY_INFO {
  4. ULONG IndentAmount;
  5. PWSTR Name;
  6. BOOLEAN NameDisplayed;
  7. HANDLE Handle;
  8. FILETIME LastWriteTime;
  9. } KEY_INFO, *PKEY_INFO;
  10. #define MAX_KEY_DEPTH 64
  11. void
  12. DisplayPath(
  13. PKEY_INFO p,
  14. ULONG Depth,
  15. PSECURITY_DESCRIPTOR SecurityDescriptor
  16. )
  17. {
  18. ULONG i;
  19. for (i=0; i<Depth-1; i++) {
  20. if (!p[ i ].NameDisplayed) {
  21. p[ i ].NameDisplayed = TRUE;
  22. RTFormatKeyName( (PREG_OUTPUT_ROUTINE)MsgFprintf, stdout, i * IndentMultiple, p[ i ].Name );
  23. if (i+1 == Depth-1) {
  24. RTFormatKeySecurity( (PREG_OUTPUT_ROUTINE)MsgFprintf,
  25. stdout,
  26. NULL,
  27. SecurityDescriptor
  28. );
  29. }
  30. MsgFprintf( stdout,"\n" );
  31. }
  32. }
  33. }
  34. LONG
  35. DeleteKeyTree(
  36. IN PREG_CONTEXT RegistryContext,
  37. IN HKEY ParentKeyHandle,
  38. IN PCWSTR KeyName
  39. )
  40. {
  41. HKEY KeyHandle;
  42. WCHAR SubKeyName[ MAX_PATH ];
  43. ULONG SubKeyNameLength;
  44. FILETIME LastWriteTime;
  45. LONG Error;
  46. Error = RTOpenKey( RegistryContext,
  47. ParentKeyHandle,
  48. KeyName,
  49. MAXIMUM_ALLOWED,
  50. REG_OPTION_OPEN_LINK,
  51. &KeyHandle
  52. );
  53. //
  54. // Enumerate node's children and apply ourselves to each one
  55. //
  56. while (Error == NO_ERROR) {
  57. SubKeyNameLength = sizeof( SubKeyName ) / sizeof(WCHAR);
  58. Error = RTEnumerateKey( RegistryContext,
  59. KeyHandle,
  60. 0,
  61. &LastWriteTime,
  62. &SubKeyNameLength,
  63. SubKeyName
  64. );
  65. if (Error == NO_ERROR) {
  66. Error = DeleteKeyTree( RegistryContext, KeyHandle, SubKeyName );
  67. }
  68. }
  69. RTCloseKey( RegistryContext, KeyHandle );
  70. return RTDeleteKey( RegistryContext,
  71. ParentKeyHandle,
  72. KeyName
  73. );
  74. }
  75. LONG
  76. InitializeRegistryFromAsciiFile(
  77. IN PWSTR FileName
  78. )
  79. {
  80. HKEY RootHandle;
  81. LONG Error, ReturnedError;
  82. REG_UNICODE_FILE UnicodeFile;
  83. REG_UNICODE_PARSE ParsedLine;
  84. ULONG OldValueType, OldValueLength;
  85. KEY_INFO KeyPath[ MAX_KEY_DEPTH ];
  86. PKEY_INFO CurrentKey;
  87. ULONG KeyPathLength;
  88. ULONG Disposition;
  89. ULONG i;
  90. ULONG PreviousValueIndentAmount;
  91. LPSTR s;
  92. Error = RTConnectToRegistry( MachineName,
  93. HiveFileName,
  94. HiveRootName,
  95. Win95Path,
  96. Win95UserPath,
  97. NULL,
  98. &RegistryContext
  99. );
  100. if (Error != NO_ERROR) {
  101. return Error;
  102. }
  103. RootHandle = RegistryContext.HiveRootHandle;
  104. Error = RTLoadAsciiFileAsUnicode( FileName,
  105. &UnicodeFile
  106. );
  107. if (Error != NO_ERROR) {
  108. RTDisconnectFromRegistry( &RegistryContext );
  109. return Error;
  110. }
  111. RtlZeroMemory( &ParsedLine, sizeof( ParsedLine ) );
  112. UnicodeFile.BackwardsCompatibleInput = BackwardsCompatibleInput;
  113. PreviousValueIndentAmount = 0xFFFFFFFF;
  114. CurrentKey = 0;
  115. KeyPathLength = 0;
  116. ReturnedError = NO_ERROR;
  117. while (TRUE) {
  118. if (!RTParseNextLine( &UnicodeFile, &ParsedLine )) {
  119. if (!ParsedLine.AtEndOfFile) {
  120. DisplayPath( KeyPath, KeyPathLength+1, NULL );
  121. if (ParsedLine.IsKeyName) {
  122. InputMessage( FileName,
  123. ParsedLine.LineNumber,
  124. TRUE,
  125. ParsedLine.AclString ? "Invalid key '%ws' Acl [%ws]"
  126. : "Invalid key '%ws'",
  127. (ULONG_PTR)ParsedLine.KeyName,
  128. (ULONG_PTR)ParsedLine.AclString
  129. );
  130. }
  131. else {
  132. switch( ParsedLine.ParseFailureReason ) {
  133. case ParseFailValueTooLarge:
  134. s = "Value too large - '%ws = %ws'";
  135. break;
  136. case ParseFailUnableToAccessFile:
  137. s = "Unable to access file - '%ws = %ws'";
  138. break;
  139. case ParseFailDateTimeFormatInvalid:
  140. s = "Date/time format invalid - '%ws = %ws'";
  141. break;
  142. case ParseFailInvalidLineContinuation:
  143. s = "Invalid line continuation - '%ws = %ws'";
  144. break;
  145. case ParseFailInvalidQuoteCharacter:
  146. s = "Invalid quote character - '%ws = %ws'";
  147. break;
  148. case ParseFailBinaryDataLengthMissing:
  149. s = "Missing length for binary data - '%ws = %ws'";
  150. break;
  151. case ParseFailBinaryDataOmitted:
  152. case ParseFailBinaryDataNotEnough:
  153. s = "Not enough binary data for length - '%ws = %ws'";
  154. break;
  155. case ParseFailInvalidRegistryType:
  156. s = "Invalid registry type - '%ws = %ws'";
  157. break;
  158. default:
  159. s = "Invalid value - '%ws = %ws'";
  160. }
  161. InputMessage( FileName,
  162. ParsedLine.LineNumber,
  163. TRUE,
  164. s,
  165. (ULONG_PTR)ParsedLine.ValueName,
  166. (ULONG_PTR)ParsedLine.ValueString
  167. );
  168. ReturnedError = ERROR_BAD_FORMAT;
  169. break;
  170. }
  171. }
  172. break;
  173. }
  174. else
  175. if (ParsedLine.IsKeyName) {
  176. if (DebugOutput) {
  177. MsgFprintf( stdout, "%02u %04u KeyName: %ws",
  178. KeyPathLength,
  179. ParsedLine.IndentAmount,
  180. ParsedLine.KeyName
  181. );
  182. }
  183. if (ParsedLine.IndentAmount > PreviousValueIndentAmount) {
  184. MsgFprintf( stderr,
  185. "REGINI: Missing line continuation character for %ws\n",
  186. ParsedLine.KeyName
  187. );
  188. ReturnedError = ERROR_BAD_FORMAT;
  189. break;
  190. }
  191. else {
  192. PreviousValueIndentAmount = 0xFFFFFFFF;
  193. }
  194. //
  195. // This fixes a 64 bit compiler problem where the statement
  196. //
  197. // CurrentKey = &KeyPath[ KeyPathLength - 1 ];
  198. //
  199. // Is evaulated as (KeyPath + (ULONG)(KeyPathLength - 1)) this is likely
  200. // because KeyPathLength is a ULONG so the result is when KeyPathLength is 0
  201. // 0xffffffff is added instead of -1
  202. //
  203. CurrentKey = &KeyPath[ KeyPathLength ];
  204. CurrentKey--;
  205. if (KeyPathLength == 0 ||
  206. ParsedLine.IndentAmount > CurrentKey->IndentAmount
  207. ) {
  208. //
  209. // If first key seen or this key is indented more than last
  210. // key, then we care going to create this key as a child of
  211. // its parent
  212. //
  213. if (KeyPathLength == MAX_KEY_DEPTH) {
  214. MsgFprintf( stderr,
  215. "REGINI: %ws key exceeded maximum depth (%u) of tree.\n",
  216. ParsedLine.KeyName,
  217. MAX_KEY_DEPTH
  218. );
  219. ReturnedError = ERROR_FILENAME_EXCED_RANGE;
  220. break;
  221. }
  222. KeyPathLength++;
  223. CurrentKey++;
  224. }
  225. else {
  226. //
  227. // Not first key seen and indented less than or same as
  228. // last key. Close any children keys to get back to
  229. // our current level.
  230. //
  231. do {
  232. RTCloseKey( &RegistryContext, CurrentKey->Handle );
  233. CurrentKey->Handle = NULL;
  234. if (ParsedLine.IndentAmount == CurrentKey->IndentAmount) {
  235. break;
  236. }
  237. CurrentKey--;
  238. if (--KeyPathLength <= 1) {
  239. break;
  240. }
  241. }
  242. while (ParsedLine.IndentAmount <= CurrentKey->IndentAmount);
  243. }
  244. if (DebugOutput) {
  245. MsgFprintf( stdout, " (%02u)\n", KeyPathLength );
  246. }
  247. CurrentKey->Name = ParsedLine.KeyName;
  248. CurrentKey->NameDisplayed = FALSE;
  249. CurrentKey->IndentAmount = ParsedLine.IndentAmount;
  250. CurrentKey->Handle = NULL;
  251. if (ParsedLine.DeleteKey) {
  252. Error = DeleteKeyTree( &RegistryContext,
  253. (KeyPathLength < 2 ? RootHandle : \
  254. KeyPath[ KeyPathLength - 2 ].Handle),
  255. ParsedLine.KeyName
  256. );
  257. if (Error == NO_ERROR) {
  258. if (DebugOutput) {
  259. MsgFprintf( stderr, " Deleted key %02x %ws (%x%08x)\n",
  260. CurrentKey->IndentAmount,
  261. CurrentKey->Name,
  262. HI_PTR(CurrentKey->Handle),
  263. LO_PTR(CurrentKey->Handle)
  264. );
  265. }
  266. DisplayPath( KeyPath, KeyPathLength+1, NULL );
  267. MsgFprintf( stderr, "; *** Deleted the above key and all of its subkeys ***\n" );
  268. }
  269. else {
  270. MsgFprintf( stderr,
  271. "REGINI: DeleteKey (%ws) relative to handle (%x%08x) failed - %u\n",
  272. ParsedLine.KeyName,
  273. (KeyPathLength < 2 ? HI_PTR(RootHandle) : \
  274. HI_PTR(KeyPath[ KeyPathLength - 2 ].Handle)),
  275. (KeyPathLength < 2 ? LO_PTR(RootHandle) : \
  276. LO_PTR(KeyPath[ KeyPathLength - 2 ].Handle)),
  277. Error
  278. );
  279. ReturnedError = Error;
  280. break;
  281. }
  282. }
  283. else {
  284. Error = RTCreateKey( &RegistryContext,
  285. (KeyPathLength < 2 ? RootHandle : \
  286. KeyPath[ KeyPathLength - 2 ].Handle),
  287. ParsedLine.KeyName,
  288. MAXIMUM_ALLOWED,
  289. 0,
  290. ParsedLine.SecurityDescriptor,
  291. (PHKEY)&CurrentKey->Handle,
  292. &Disposition
  293. );
  294. if (Error == NO_ERROR) {
  295. if (DebugOutput) {
  296. MsgFprintf( stderr, " Created key %02x %ws (%x%08x)\n",
  297. CurrentKey->IndentAmount,
  298. CurrentKey->Name,
  299. HI_PTR(CurrentKey->Handle),
  300. LO_PTR(CurrentKey->Handle)
  301. );
  302. }
  303. Error = RTQueryKey( &RegistryContext,
  304. CurrentKey->Handle,
  305. &CurrentKey->LastWriteTime,
  306. NULL,
  307. NULL
  308. );
  309. if (Error != NO_ERROR) {
  310. RtlZeroMemory( &CurrentKey->LastWriteTime,
  311. sizeof( CurrentKey->LastWriteTime )
  312. );
  313. }
  314. if (Disposition == REG_CREATED_NEW_KEY) {
  315. DisplayPath( KeyPath, KeyPathLength+1, ParsedLine.SecurityDescriptor );
  316. }
  317. }
  318. else {
  319. MsgFprintf( stderr,
  320. "REGINI: CreateKey (%ws) relative to handle (%x%08x) failed - %u\n",
  321. ParsedLine.KeyName,
  322. (KeyPathLength < 2 ?
  323. HI_PTR(RootHandle) :
  324. HI_PTR(KeyPath[ KeyPathLength - 2 ].Handle)),
  325. (KeyPathLength < 2 ?
  326. LO_PTR(RootHandle) :
  327. LO_PTR(KeyPath[ KeyPathLength - 2 ].Handle)),
  328. Error
  329. );
  330. ReturnedError = Error;
  331. break;
  332. }
  333. }
  334. }
  335. else {
  336. //
  337. // Have a value. If no current key, then an error
  338. //
  339. if (CurrentKey == NULL) {
  340. InputMessage( FileName,
  341. ParsedLine.LineNumber,
  342. TRUE,
  343. "Value name ('%ws') seen before any key name",
  344. (ULONG_PTR)ParsedLine.ValueName,
  345. 0
  346. );
  347. ReturnedError = ERROR_BAD_FORMAT;
  348. break;
  349. }
  350. //
  351. // Make sure this value goes under the appropriate key. That is
  352. // underneath the key that has an indentation that is <= the
  353. // key.
  354. //
  355. while (ParsedLine.IndentAmount < CurrentKey->IndentAmount) {
  356. if (DebugOutput) {
  357. MsgFprintf( stderr, " Popping from key %02x %ws (%x%08x)\n",
  358. CurrentKey->IndentAmount,
  359. CurrentKey->Name,
  360. HI_PTR(CurrentKey->Handle),
  361. LO_PTR(CurrentKey->Handle)
  362. );
  363. }
  364. RTCloseKey( &RegistryContext, CurrentKey->Handle );
  365. CurrentKey->Handle = NULL;
  366. CurrentKey--;
  367. if (--KeyPathLength <= 1) {
  368. break;
  369. }
  370. }
  371. if (DebugOutput) {
  372. MsgFprintf( stderr, " Adding value '%ws = %ws' to key %02x %ws (%x%08x)\n",
  373. ParsedLine.ValueName,
  374. ParsedLine.ValueString,
  375. CurrentKey->IndentAmount,
  376. CurrentKey->Name,
  377. HI_PTR(CurrentKey->Handle),
  378. LO_PTR(CurrentKey->Handle)
  379. );
  380. }
  381. PreviousValueIndentAmount = ParsedLine.IndentAmount;
  382. if (ParsedLine.DeleteValue) {
  383. Error = RTDeleteValueKey( &RegistryContext,
  384. KeyPath[ KeyPathLength - 1 ].Handle,
  385. ParsedLine.ValueName
  386. );
  387. if (Error == NO_ERROR) {
  388. MsgFprintf( stdout, " %ws = DELETED\n", ParsedLine.ValueName );
  389. }
  390. }
  391. else {
  392. OldValueLength = OldValueBufferSize;
  393. Error = RTQueryValueKey( &RegistryContext,
  394. KeyPath[ KeyPathLength - 1 ].Handle,
  395. ParsedLine.ValueName,
  396. &OldValueType,
  397. &OldValueLength,
  398. OldValueBuffer
  399. );
  400. if (Error != NO_ERROR ||
  401. OldValueType != ParsedLine.ValueType ||
  402. OldValueLength != ParsedLine.ValueLength ||
  403. RtlCompareMemory( OldValueBuffer,
  404. ParsedLine.ValueData,
  405. ParsedLine.ValueLength
  406. ) != ParsedLine.ValueLength
  407. ) {
  408. Error = RTSetValueKey( &RegistryContext,
  409. KeyPath[ KeyPathLength - 1 ].Handle,
  410. ParsedLine.ValueName,
  411. ParsedLine.ValueType,
  412. ParsedLine.ValueLength,
  413. ParsedLine.ValueData
  414. );
  415. if (Error == NO_ERROR) {
  416. DisplayPath( KeyPath, KeyPathLength+1, NULL );
  417. RTFormatKeyValue( OutputWidth,
  418. (PREG_OUTPUT_ROUTINE)MsgFprintf,
  419. stdout,
  420. FALSE,
  421. KeyPathLength * IndentMultiple,
  422. ParsedLine.ValueName,
  423. ParsedLine.ValueLength,
  424. ParsedLine.ValueType,
  425. ParsedLine.ValueData
  426. );
  427. }
  428. else {
  429. MsgFprintf( stderr,
  430. "REGINI: SetValueKey (%ws) failed (%u)\n",
  431. ParsedLine.ValueName,
  432. Error
  433. );
  434. ReturnedError = Error;
  435. break;
  436. }
  437. }
  438. }
  439. }
  440. }
  441. //
  442. // Close handles we still have open.
  443. //
  444. while (CurrentKey >= KeyPath ) {
  445. RTCloseKey( &RegistryContext, CurrentKey->Handle );
  446. --CurrentKey;
  447. }
  448. RTDisconnectFromRegistry( &RegistryContext );
  449. return ReturnedError;
  450. }
  451. BOOL
  452. CtrlCHandler(
  453. IN ULONG CtrlType
  454. )
  455. {
  456. RTDisconnectFromRegistry( &RegistryContext );
  457. return FALSE;
  458. }
  459. int
  460. __cdecl
  461. main(
  462. int argc,
  463. char *argv[]
  464. )
  465. {
  466. ULONG n;
  467. char *s;
  468. LONG Error;
  469. BOOL FileArgumentSeen;
  470. PWSTR FileName;
  471. InitCommonCode( CtrlCHandler,
  472. "REGINI",
  473. "[-b] textFiles...",
  474. "-b specifies that REGINI should be backward compatible with older\n"
  475. " versions of REGINI that did not strictly enforce line continuations\n"
  476. " and quoted strings Specifically, REG_BINARY, REG_RESOURCE_LIST and\n"
  477. " REG_RESOURCE_REQUIREMENTS_LIST data types did not need line\n"
  478. " continuations after the first number that gave the size of the data.\n"
  479. " It just kept looking on following lines until it found enough data\n"
  480. " values to equal the data length or hit invalid input. Quoted\n"
  481. " strings were only allowed in REG_MULTI_SZ. They could not be\n"
  482. " specified around key or value names, or around values for REG_SZ or\n"
  483. " REG_EXPAND_SZ Finally, the old REGINI did not support the semicolon\n"
  484. " as an end of line comment character.\n"
  485. "\n"
  486. "textFiles is one or more ANSI or Unicode text files with registry data.\n"
  487. "\n"
  488. "The easiest way to understand the format of the input textFile is to use\n"
  489. "the REGDMP command with no arguments to dump the current contents of\n"
  490. "your NT Registry to standard out. Redirect standard out to a file and\n"
  491. "this file is acceptable as input to REGINI\n"
  492. "\n"
  493. "Some general rules are:\n"
  494. " Semicolon character is an end-of-line comment character, provided it\n"
  495. " is the first non-blank character on a line\n"
  496. "\n"
  497. " Backslash character is a line continuation character. All\n"
  498. " characters from the backslash up to but not including the first\n"
  499. " non-blank character of the next line are ignored. If there is more\n"
  500. " than one space before the line continuation character, it is\n"
  501. " replaced by a single space.\n"
  502. "\n"
  503. " Indentation is used to indicate the tree structure of registry keys\n"
  504. " The REGDMP program uses indentation in multiples of 4. You may use\n"
  505. " hard tab characters for indentation, but embedded hard tab\n"
  506. " characters are converted to a single space regardless of their\n"
  507. " position\n"
  508. " \n"
  509. " Values should come before child keys, as they are associated with\n"
  510. " the previous key at or above the value's indentation level.\n"
  511. "\n"
  512. " For key names, leading and trailing space characters are ignored and\n"
  513. " not included in the key name, unless the key name is surrounded by\n"
  514. " quotes. Imbedded spaces are part of a key name.\n"
  515. "\n"
  516. " Key names can be followed by an Access Control List (ACL) which is a\n"
  517. " series of decimal numbers, separated by spaces, bracketed by a\n"
  518. " square brackets (e.g. [8 4 17]). The valid numbers and their\n"
  519. " meanings are:\n"
  520. "\n"
  521. " 1 - Administrators Full Access\n"
  522. " 2 - Administrators Read Access\n"
  523. " 3 - Administrators Read and Write Access\n"
  524. " 4 - Administrators Read, Write and Delete Access\n"
  525. " 5 - Creator Full Access\n"
  526. " 6 - Creator Read and Write Access\n"
  527. " 7 - World Full Access\n"
  528. " 8 - World Read Access\n"
  529. " 9 - World Read and Write Access\n"
  530. " 10 - World Read, Write and Delete Access\n"
  531. " 11 - Power Users Full Access\n"
  532. " 12 - Power Users Read and Write Access\n"
  533. " 13 - Power Users Read, Write and Delete Access\n"
  534. " 14 - System Operators Full Access\n"
  535. " 15 - System Operators Read and Write Access\n"
  536. " 16 - System Operators Read, Write and Delete Access\n"
  537. " 17 - System Full Access\n"
  538. " 18 - System Read and Write Access\n"
  539. " 19 - System Read Access\n"
  540. " 20 - Administrators Read, Write and Execute Access\n"
  541. " 21 - Interactive User Full Access\n"
  542. " 22 - Interactive User Read and Write Access\n"
  543. " 23 - Interactive User Read, Write and Delete Access\n"
  544. "\n"
  545. " If there is an equal sign on the same line as a left square bracket\n"
  546. " then the equal sign takes precedence, and the line is treated as a\n"
  547. " registry value. If the text between the square brackets is the\n"
  548. " string DELETE with no spaces, then REGINI will delete the key and\n"
  549. " any values and keys under it.\n"
  550. "\n"
  551. " For registry values, the syntax is:\n"
  552. "\n"
  553. " value Name = type data\n"
  554. "\n"
  555. " Leading spaces, spaces on either side of the equal sign and spaces\n"
  556. " between the type keyword and data are ignored, unless the value name\n"
  557. " is surrounded by quotes. If the text to the right of the equal sign\n"
  558. " is the string DELETE, then REGINI will delete the value.\n"
  559. "\n"
  560. " The value name may be left off or be specified by an at-sign\n"
  561. " character which is the same thing, namely the empty value name. So\n"
  562. " the following two lines are identical:\n"
  563. "\n"
  564. " = type data\n"
  565. " @ = type data\n"
  566. "\n"
  567. " This syntax means that you can't create a value with leading or\n"
  568. " trailing spaces, an equal sign or an at-sign in the value name,\n"
  569. " unless you put the name in quotes.\n"
  570. "\n"
  571. " Valid value types and format of data that follows are:\n"
  572. "\n"
  573. " REG_SZ text\n"
  574. " REG_EXPAND_SZ text\n"
  575. " REG_MULTI_SZ \"string1\" \"str\"\"ing2\" ...\n"
  576. " REG_DATE mm/dd/yyyy HH:MM DayOfWeek\n"
  577. " REG_DWORD numberDWORD\n"
  578. " REG_BINARY numberOfBytes numberDWORD(s)...\n"
  579. " REG_NONE (same format as REG_BINARY)\n"
  580. " REG_RESOURCE_LIST (same format as REG_BINARY)\n"
  581. " REG_RESOURCE_REQUIREMENTS (same format as REG_BINARY)\n"
  582. " REG_RESOURCE_REQUIREMENTS_LIST (same format as REG_BINARY)\n"
  583. " REG_FULL_RESOURCE_DESCRIPTOR (same format as REG_BINARY)\n"
  584. " REG_MULTISZ_FILE fileName\n"
  585. " REG_BINARYFILE fileName\n"
  586. "\n"
  587. " If no value type is specified, default is REG_SZ\n"
  588. "\n"
  589. " For REG_SZ and REG_EXPAND_SZ, if you want leading or trailing spaces\n"
  590. " in the value text, surround the text with quotes. The value text\n"
  591. " can contain any number of imbedded quotes, and REGINI will ignore\n"
  592. " them, as it only looks at the first and last character for quote\n"
  593. " characters.\n"
  594. "\n"
  595. " For REG_MULTI_SZ, each component string is surrounded by quotes. If\n"
  596. " you want an imbedded quote character, then double quote it, as in\n"
  597. " string2 above.\n"
  598. "\n"
  599. " For REG_BINARY, the value data consists of one or more numbers The\n"
  600. " default base for numbers is decimal. Hexidecimal may be specified\n"
  601. " by using 0x prefix. The first number is the number of data bytes,\n"
  602. " excluding the first number. After the first number must come enough\n"
  603. " numbers to fill the value. Each number represents one DWORD or 4\n"
  604. " bytes. So if the first number was 0x5 you would need two more\n"
  605. " numbers after that to fill the 5 bytes. The high order 3 bytes\n"
  606. " of the second DWORD would be ignored.\n"
  607. );
  608. BackwardsCompatibleInput = FALSE;
  609. FileArgumentSeen = FALSE;
  610. while (--argc) {
  611. s = *++argv;
  612. if (*s == '-' || *s == '/') {
  613. while (*++s) {
  614. switch( tolower( *s ) ) {
  615. case 'b':
  616. BackwardsCompatibleInput = TRUE;
  617. break;
  618. default:
  619. CommonSwitchProcessing( &argc, &argv, *s );
  620. }
  621. }
  622. }
  623. else {
  624. FileArgumentSeen = TRUE;
  625. FileName = GetArgAsUnicode( s );
  626. if (FileName == NULL) {
  627. Error = GetLastError();
  628. }
  629. else {
  630. Error = InitializeRegistryFromAsciiFile( FileName );
  631. }
  632. if (Error != NO_ERROR) {
  633. FatalError( "Failed to load from file '%s' (%u)\n",
  634. (ULONG_PTR)s, Error );
  635. exit( Error );
  636. }
  637. }
  638. }
  639. if (!FileArgumentSeen) {
  640. Usage( "No textFile specified", 0 );
  641. }
  642. return 0;
  643. }