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.

2423 lines
63 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. dncopy.c
  5. Abstract:
  6. File copy routines for DOS-hosted NT Setup program.
  7. Author:
  8. Ted Miller (tedm) 1-April-1992
  9. Revision History:
  10. 4.0 Stephane Plante (t-stepl) 11-Dec-95
  11. Upgraded for SUR Release
  12. --*/
  13. #include "winnt.h"
  14. #include <dos.h>
  15. #include <fcntl.h>
  16. #include <share.h>
  17. #include <string.h>
  18. #include <direct.h>
  19. #include <ctype.h>
  20. //
  21. // Define size of buffer we initially try to allocate for file copies
  22. // and the size we use if the initial allocation attempt fails.
  23. //
  24. #define COPY_BUFFER_SIZE1 (24*1024) // 64K - 512
  25. #define COPY_BUFFER_SIZE2 (24*1024) // 24K
  26. #define COPY_BUFFER_SIZE3 (8*1024) // 8K
  27. typedef struct _DIRECTORY_NODE {
  28. struct _DIRECTORY_NODE *Next;
  29. PCHAR Directory; // never starts or ends with \.
  30. PCHAR Symbol;
  31. } DIRECTORY_NODE, *PDIRECTORY_NODE;
  32. PDIRECTORY_NODE DirectoryList;
  33. PSCREEN CopyingScreen;
  34. BOOLEAN UsingGauge = FALSE;
  35. //
  36. // Total number of files to be copied
  37. //
  38. unsigned TotalFileCount;
  39. //
  40. // Total number of files in the optional directories
  41. //
  42. unsigned TotalOptionalFileCount = 0;
  43. //
  44. // Total number of optional directories
  45. //
  46. unsigned TotalOptionalDirCount = 0;
  47. #if 0
  48. // Debugging purposes
  49. unsigned SaveTotalOptionalDirCount = 0;
  50. #endif
  51. //
  52. // Buffer used for file copying and verifying,
  53. // and the size of the buffer.
  54. //
  55. PVOID CopyBuffer;
  56. unsigned CopyBufferSize;
  57. VOID
  58. DnpCreateDirectoryList(
  59. IN PCHAR SectionName,
  60. OUT PDIRECTORY_NODE *ListHead
  61. );
  62. VOID
  63. DnpCreateDirectories(
  64. IN PCHAR TargetRootDir
  65. );
  66. VOID
  67. DnpCreateOneDirectory(
  68. IN PCHAR Directory
  69. );
  70. BOOLEAN
  71. DnpOpenSourceFile(
  72. IN PCHAR Filename,
  73. OUT int *Handle,
  74. OUT unsigned *Attribs,
  75. OUT BOOLEAN *UsedCompressedName
  76. );
  77. ULONG
  78. DnpIterateCopyList(
  79. IN unsigned Flags,
  80. IN PCHAR SectionName,
  81. IN PCHAR DestinationRoot,
  82. IN unsigned ClusterSize OPTIONAL
  83. );
  84. ULONG
  85. DnpIterateCopyListSection(
  86. IN unsigned Flags,
  87. IN PCHAR SectionName,
  88. IN PCHAR DestinationRoot,
  89. IN unsigned ClusterSize OPTIONAL
  90. );
  91. ULONG
  92. DnpCopyOneFile(
  93. IN unsigned Flags,
  94. IN PCHAR SourceName,
  95. IN PCHAR DestName
  96. );
  97. BOOLEAN
  98. DnpDoCopyOneFile(
  99. IN unsigned Flags,
  100. IN int SrcHandle,
  101. IN int DstHandle,
  102. IN PCHAR Filename,
  103. OUT PBOOLEAN Verified,
  104. OUT PULONG BytesWritten
  105. );
  106. BOOLEAN
  107. DnpLookUpDirectory(
  108. IN PCHAR RootDirectory,
  109. IN PDIRECTORY_NODE DirList,
  110. IN PCHAR Symbol,
  111. OUT PCHAR PathOut
  112. );
  113. VOID
  114. DnpInfSyntaxError(
  115. IN PCHAR Section
  116. );
  117. VOID
  118. DnpFreeDirectoryList(
  119. IN OUT PDIRECTORY_NODE *List
  120. );
  121. VOID
  122. DnpFormatSpaceErrMsg(
  123. IN ULONG NtSpaceReq,
  124. IN ULONG CSpaceReq
  125. );
  126. ULONG
  127. DnpDoIterateOptionalDir(
  128. IN unsigned Flags,
  129. IN PCHAR SourceDir,
  130. IN PCHAR DestDir,
  131. IN unsigned ClusterSize OPTIONAL,
  132. IN PSPACE_REQUIREMENT SpaceReqArray OPTIONAL,
  133. IN unsigned ArraySize OPTIONAL
  134. );
  135. VOID
  136. DnpGenerateCompressedName(
  137. IN PCHAR Filename,
  138. OUT PCHAR CompressedName
  139. );
  140. BOOLEAN
  141. InDriverCacheInf(
  142. IN PVOID InfHandle,
  143. IN PCHAR FileName
  144. );
  145. VOID
  146. DnpConcatPaths(
  147. IN PCHAR SourceBuffer,
  148. IN PCHAR AppendString
  149. )
  150. {
  151. if (SourceBuffer[strlen(SourceBuffer) -1] != '\\') {
  152. strcat(SourceBuffer, "\\");
  153. }
  154. strcat(SourceBuffer,AppendString);
  155. }
  156. #if NEC_98
  157. ULONG
  158. DnpCopyOneFileForFDless(
  159. IN PCHAR SourceName,
  160. IN PCHAR DestName,
  161. IN BOOLEAN Verify
  162. );
  163. #endif // NEC_98
  164. VOID
  165. DnCopyFiles(
  166. VOID
  167. )
  168. /*++
  169. Routine Description:
  170. Top-level file copy entry point. Creates all directories listed in
  171. the [Directories] section of the inf. Copies all files listed in the
  172. [Files] section of the inf file from the source to the target (which
  173. becomes the local source).
  174. Arguments:
  175. None.
  176. Return Value:
  177. None.
  178. --*/
  179. {
  180. PCHAR LocalSourceRoot;
  181. struct diskfree_t DiskFree;
  182. unsigned ClusterSize;
  183. ULONG SizeOccupied;
  184. PCHAR UdfFileName = WINNT_UNIQUENESS_DB;
  185. PCHAR UdfPath;
  186. //
  187. // Do not change this without changing text setup as well
  188. // (SpPtDetermineRegionSpace()).
  189. //
  190. PCHAR SizeFile = "\\size.sif";
  191. PCHAR Lines[] = { "[Data]\n",
  192. "Size = xxxxxxxxxxxxxx\n",
  193. //
  194. // Debugging purposes
  195. //
  196. "TotalFileCount = xxxxxxxxxxxxxx\n",
  197. "TotalOptionalFileCount = xxxxxxxxxxxxxx\n",
  198. "TotalOptionalDirCount = xxxxxxxxxxxxxx\n",
  199. "ClusterSize = xxxxxxxxxxxxxx\n",
  200. "Size_512 = xxxxxxxxxxxxxx\n",
  201. "Size_1K = xxxxxxxxxxxxxx\n",
  202. "Size_2K = xxxxxxxxxxxxxx\n",
  203. "Size_4K = xxxxxxxxxxxxxx\n",
  204. "Size_8K = xxxxxxxxxxxxxx\n",
  205. "Size_16K = xxxxxxxxxxxxxx\n",
  206. "Size_32K = xxxxxxxxxxxxxx\n",
  207. #if 0
  208. "SaveTotalOptionalDirCount = xxxxxxxxxxxxxx\n",
  209. #endif
  210. NULL };
  211. DnClearClientArea();
  212. DnDisplayScreen(CopyingScreen = &DnsWaitCopying);
  213. DnWriteStatusText(NULL);
  214. //
  215. // Create the linked list of directories.
  216. //
  217. DnpCreateDirectoryList(DnfDirectories,&DirectoryList);
  218. //
  219. // Generate the full root path of the local source
  220. //
  221. LocalSourceRoot = MALLOC(sizeof(LOCAL_SOURCE_DIRECTORY) + strlen(x86DirName) + strlen(SizeFile),TRUE);
  222. LocalSourceRoot[0] = DngTargetDriveLetter;
  223. LocalSourceRoot[1] = ':';
  224. strcpy(LocalSourceRoot+2,LocalSourceDirName);
  225. DnpCreateOneDirectory(LocalSourceRoot);
  226. //
  227. // Yuck. Create this directory here because when
  228. // we're running down the main copylist, we're expecting
  229. // no non-existent directories in the destination name.
  230. // There can be though. The right fix is to fix DnCopyOneFile
  231. // to create directories if he finds them in the destination string
  232. // sent in. This is a faster fix though.
  233. //
  234. {
  235. char MyLocalSourceRoot[256];
  236. strcpy( MyLocalSourceRoot, LocalSourceRoot );
  237. strcat( MyLocalSourceRoot, x86DirName );
  238. DnpCreateOneDirectory(MyLocalSourceRoot);
  239. strcat( MyLocalSourceRoot, "\\System32" );
  240. DnpCreateOneDirectory(MyLocalSourceRoot);
  241. }
  242. if(UniquenessDatabaseFile) {
  243. UdfPath = MALLOC(strlen(LocalSourceRoot) + strlen(UdfFileName) + 2, TRUE);
  244. strcpy(UdfPath,LocalSourceRoot);
  245. DnpConcatPaths(UdfPath,UdfFileName);
  246. DnpCopyOneFile(CPY_PRESERVE_ATTRIBS,UniquenessDatabaseFile,UdfPath);
  247. FREE(UdfPath);
  248. }
  249. // don't need this appendage anymore - part of changes for 2CD setup.
  250. #if 0
  251. strcat(LocalSourceRoot,x86DirName);
  252. #endif
  253. //
  254. // Determine the cluster size on the drive.
  255. //
  256. _dos_getdiskfree(toupper(DngTargetDriveLetter)-'A'+1,&DiskFree);
  257. ClusterSize = DiskFree.sectors_per_cluster * DiskFree.bytes_per_sector;
  258. //
  259. // Pass over the copy list and check syntax.
  260. // Note that the global variable TotalOptionalFileCount is already set
  261. // (this was done when we determined the disk space requirements), and we
  262. // no longer need to call DnpIterateOptionalDirs() in the validation mode
  263. //
  264. DnpIterateCopyList(CPY_VALIDATION_PASS | CPY_PRUNE_DRIVERCAB,DnfFiles,LocalSourceRoot,0);
  265. //
  266. // TotalFileCount must indicate the total number of files in the flat
  267. // directory and optional directories.
  268. //
  269. TotalFileCount += TotalOptionalFileCount;
  270. //
  271. // Create the target directories
  272. //
  273. DnpCreateDirectories(LocalSourceRoot);
  274. //
  275. // Pass over the copy list again and actually perform the copy.
  276. //
  277. UsingGauge = TRUE;
  278. SizeOccupied = DnpIterateCopyList(CPY_PRESERVE_NAME | CPY_PRUNE_DRIVERCAB,DnfFiles,LocalSourceRoot,ClusterSize);
  279. SizeOccupied += DnpIterateOptionalDirs(0,ClusterSize,NULL,0);
  280. //
  281. // Free the copy buffer.
  282. //
  283. if(CopyBuffer) {
  284. FREE(CopyBuffer);
  285. CopyBuffer = NULL;
  286. }
  287. //
  288. // Free the directory node list
  289. //
  290. DnpFreeDirectoryList(&DirectoryList);
  291. //
  292. // Make an approximate calculation of the amount of disk space taken up
  293. // by the local source directory itself, assuming 32 bytes per dirent.
  294. // Also account for the small ini file that we'll put in the local source
  295. // directory, to tell text setup how much space the local source takes up.
  296. //
  297. // Takes into consideration the dirent for each file in the flat directory
  298. // plust the directories . and .., plus size.sif, plus $win_nt_.~ls and
  299. // $win_nt$.~ls\i386
  300. //
  301. SizeOccupied += ((((TotalFileCount - TotalOptionalFileCount) + // number of files in the $win_nt$.~ls\i386 directory
  302. 1 + // $win_nt$.~ls
  303. 2 + // . and .. on $win_nt$.~ls
  304. 1 + // size.sif on $win_nt$.~ls
  305. 1 + // $win_nt$.~ls\i386
  306. 2 // . and .. on $win_nt$.~ls\i386
  307. )*32 + (ClusterSize-1)) / ClusterSize)*ClusterSize;
  308. //
  309. // Now take into consideration the optional directories.
  310. //
  311. if(TotalOptionalDirCount != 0) {
  312. unsigned AvFilesPerOptionalDir= 0;
  313. //
  314. // We assume a uniform distribution of optional files on optional
  315. // directories
  316. //
  317. AvFilesPerOptionalDir = (TotalOptionalFileCount + (TotalOptionalDirCount - 1))/TotalOptionalDirCount;
  318. AvFilesPerOptionalDir += 2; // . and .. on each optional dir
  319. SizeOccupied += (TotalOptionalDirCount*((AvFilesPerOptionalDir*32 + (ClusterSize-1))/ClusterSize))*ClusterSize;
  320. //
  321. // Finally take into account each optional directory
  322. //
  323. SizeOccupied += ((TotalOptionalDirCount*32 + (ClusterSize-1))/ClusterSize)*ClusterSize;
  324. }
  325. //
  326. // Create a small ini file listing the size occupied by the local source.
  327. // Account for the ini file in the size.
  328. //
  329. strcpy(LocalSourceRoot+2,LocalSourceDirName);
  330. strcat(LocalSourceRoot,SizeFile);
  331. sprintf(Lines[1],"Size = %lu\n",SizeOccupied);
  332. //
  333. // Debugging purposes
  334. //
  335. sprintf(Lines[2], "TotalFileCount = %u\n" ,TotalFileCount);
  336. sprintf(Lines[3], "TotalOptionalFileCount = %u\n" ,TotalOptionalFileCount);
  337. sprintf(Lines[4], "TotalOptionalDirCount = %u\n" ,TotalOptionalDirCount);
  338. sprintf(Lines[5], "ClusterSize = %u\n" , ClusterSize);
  339. sprintf(Lines[6], "Size_%u = %lu\n" , SpaceRequirements[0].ClusterSize, SpaceRequirements[0].Clusters * SpaceRequirements[0].ClusterSize);
  340. sprintf(Lines[7], "Size_%u = %lu\n" , SpaceRequirements[1].ClusterSize, SpaceRequirements[1].Clusters * SpaceRequirements[1].ClusterSize);
  341. sprintf(Lines[8], "Size_%u = %lu\n" , SpaceRequirements[2].ClusterSize, SpaceRequirements[2].Clusters * SpaceRequirements[2].ClusterSize);
  342. sprintf(Lines[9], "Size_%u = %lu\n" , SpaceRequirements[3].ClusterSize, SpaceRequirements[3].Clusters * SpaceRequirements[3].ClusterSize);
  343. sprintf(Lines[10],"Size_%u = %lu\n" , SpaceRequirements[4].ClusterSize, SpaceRequirements[4].Clusters * SpaceRequirements[4].ClusterSize);
  344. sprintf(Lines[11],"Size_%u = %lu\n" , SpaceRequirements[5].ClusterSize, SpaceRequirements[5].Clusters * SpaceRequirements[5].ClusterSize);
  345. sprintf(Lines[12],"Size_%u = %lu\n" , SpaceRequirements[6].ClusterSize, SpaceRequirements[6].Clusters * SpaceRequirements[6].ClusterSize);
  346. #if 0
  347. sprintf(Lines[13],"SaveTotalOptionalDirCount = %u\n" ,SaveTotalOptionalDirCount);
  348. #endif
  349. DnWriteSmallIniFile(LocalSourceRoot,Lines,NULL);
  350. FREE(LocalSourceRoot);
  351. }
  352. VOID
  353. DnCopyFloppyFiles(
  354. IN PCHAR SectionName,
  355. IN PCHAR TargetRoot
  356. )
  357. /*++
  358. Routine Description:
  359. Top-level entry point to copy files to the setup floppy or hard-disk
  360. boot root when this routine is called. Copies all files listed in the
  361. [FloppyFiles.x] sections of the inf file from the source to TargetRoot.
  362. Arguments:
  363. SectionName - supplies the name of the section in the inf file
  364. that contains the list of files to be copied.
  365. TargetRoot - supplies the target path without trailing \.
  366. Return Value:
  367. None.
  368. --*/
  369. {
  370. DnClearClientArea();
  371. DnDisplayScreen(CopyingScreen = (DngFloppyless ? &DnsWaitCopying : &DnsWaitCopyFlop));
  372. DnWriteStatusText(NULL);
  373. //
  374. // Create the linked list of directories.
  375. //
  376. DnpCreateDirectoryList(DnfDirectories,&DirectoryList);
  377. //
  378. // Copy the files.
  379. //
  380. DnpIterateCopyList(
  381. CPY_VALIDATION_PASS | CPY_USE_DEST_ROOT,
  382. SectionName,
  383. TargetRoot,
  384. 0
  385. );
  386. DnpIterateCopyList(
  387. CPY_USE_DEST_ROOT | CPY_PRESERVE_NAME | (DngFloppyVerify ? CPY_VERIFY : 0),
  388. SectionName,
  389. TargetRoot,
  390. 0
  391. );
  392. //
  393. // Free the copy buffer.
  394. //
  395. if(CopyBuffer) {
  396. FREE(CopyBuffer);
  397. CopyBuffer = NULL;
  398. }
  399. //
  400. // Free the directory node list
  401. //
  402. DnpFreeDirectoryList(&DirectoryList);
  403. }
  404. VOID
  405. DnpCreateDirectoryList(
  406. IN PCHAR SectionName,
  407. OUT PDIRECTORY_NODE *ListHead
  408. )
  409. /*++
  410. Routine Description:
  411. Examine a section in the INF file, whose lines are to be in the form
  412. key = directory and create a linked list describing the key/directory
  413. pairs found therein.
  414. If the directory field is empty, it is assumed to be the root.
  415. Arguments:
  416. SectionName - supplies name of section
  417. ListHead - receives pointer to head of linked list
  418. Return Value:
  419. None. Does not return if syntax error in the inf file section.
  420. --*/
  421. {
  422. unsigned LineIndex,len;
  423. PDIRECTORY_NODE DirNode,PreviousNode;
  424. PCHAR Key;
  425. PCHAR Dir;
  426. PCHAR Dir1;
  427. LineIndex = 0;
  428. PreviousNode = NULL;
  429. while(Key = DnGetKeyName(DngInfHandle,SectionName,LineIndex)) {
  430. Dir1 = DnGetSectionKeyIndex(DngInfHandle,SectionName,Key,0);
  431. if(Dir1 == NULL) {
  432. Dir = ""; // use the root if not specified
  433. }
  434. else {
  435. Dir = Dir1;
  436. }
  437. // 2CD setup changes - We can't do this relative roots anymore.
  438. // All of the directories have to be absolute to the LocalSource root.
  439. // We probably need to enforce that there is a '\' in the front.
  440. #if 0
  441. //
  442. // Skip leading backslashes
  443. //
  444. while(*Dir == '\\') {
  445. Dir++;
  446. }
  447. #endif
  448. //
  449. // Clip off trailing backslashes if present
  450. //
  451. while((len = strlen(Dir)) && (Dir[len-1] == '\\')) {
  452. Dir[len-1] = '\0';
  453. }
  454. DirNode = MALLOC(sizeof(DIRECTORY_NODE),TRUE);
  455. DirNode->Next = NULL;
  456. DirNode->Directory = DnDupString(Dir);
  457. DirNode->Symbol = DnDupString(Key);
  458. if(PreviousNode) {
  459. PreviousNode->Next = DirNode;
  460. } else {
  461. *ListHead = DirNode;
  462. }
  463. PreviousNode = DirNode;
  464. LineIndex++;
  465. FREE (Dir1);
  466. FREE (Key);
  467. }
  468. }
  469. VOID
  470. DnpCreateDirectories(
  471. IN PCHAR TargetRootDir
  472. )
  473. /*++
  474. Routine Description:
  475. Create the local source directory, and run down the DirectoryList and
  476. create directories listed therein relative to the given root dir.
  477. Arguments:
  478. TargetRootDir - supplies the name of root directory of the target
  479. Return Value:
  480. None.
  481. --*/
  482. {
  483. PDIRECTORY_NODE DirNode;
  484. CHAR TargetDirTemp[128];
  485. DnpCreateOneDirectory(TargetRootDir);
  486. for(DirNode = DirectoryList; DirNode; DirNode = DirNode->Next) {
  487. if ( DngCopyOnlyD1TaggedFiles )
  488. {
  489. // symbol will always exist, so we are safe in using it in
  490. // the comparision below.
  491. // We skip this file if the tag is not d1
  492. if ( strcmpi(DirNode->Symbol, "d1") )
  493. continue;
  494. }
  495. //
  496. // No need to create the root
  497. //
  498. if(*DirNode->Directory) {
  499. // 2 CD Setup changes - if Directory is of the a\b\c\d then we better make sure
  500. // we can create the entire directory structure
  501. // We run into this when \cmpnents\tabletpc\i386 needs to be created.
  502. CHAR *pCurDir = DirNode->Directory;
  503. CHAR *pTargetDir;
  504. strcpy(TargetDirTemp,TargetRootDir);
  505. strcat(TargetDirTemp,"\\");
  506. pCurDir++; // the first character is always bound to be '\'
  507. pTargetDir = TargetDirTemp + strlen(TargetDirTemp);
  508. #if 0
  509. strcat(TargetDirTemp,DirNode->Directory);
  510. #else
  511. for ( ; *pCurDir; pTargetDir++, pCurDir++ )
  512. {
  513. if ( *pCurDir == '\\' )
  514. {
  515. *pTargetDir = 0;
  516. DnpCreateOneDirectory(TargetDirTemp);
  517. }
  518. *pTargetDir = *pCurDir;
  519. }
  520. *pTargetDir = 0;
  521. #endif
  522. DnpCreateOneDirectory(TargetDirTemp);
  523. }
  524. }
  525. }
  526. VOID
  527. DnpCreateOneDirectory(
  528. IN PCHAR Directory
  529. )
  530. /*++
  531. Routine Description:
  532. Create a single directory if it does not already exist.
  533. Arguments:
  534. Directory - directory to create
  535. Return Value:
  536. None. Does not return if directory cannot be created.
  537. --*/
  538. {
  539. struct find_t FindBuf;
  540. int Status;
  541. //
  542. // First, see if there's a file out there that matches the name.
  543. //
  544. Status = _dos_findfirst( Directory,
  545. _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR,
  546. &FindBuf
  547. );
  548. if(Status) {
  549. //
  550. // file could not be matched so we should be able to create the dir.
  551. //
  552. if(mkdir(Directory)) {
  553. DnFatalError(&DnsCantCreateDir,Directory);
  554. }
  555. } else {
  556. //
  557. // file matched. If it's a dir, we're OK. Otherwise we can't
  558. // create the dir, a fatal error.
  559. //
  560. if(FindBuf.attrib & _A_SUBDIR) {
  561. return;
  562. } else {
  563. DnFatalError(&DnsCantCreateDir,Directory);
  564. }
  565. }
  566. }
  567. ULONG
  568. DnpIterateCopyList(
  569. IN unsigned Flags,
  570. IN PCHAR SectionName,
  571. IN PCHAR DestinationRoot,
  572. IN unsigned ClusterSize OPTIONAL
  573. )
  574. /*++
  575. Routine Description:
  576. Run through the NtTreeFiles and BootFiles sections, validating their
  577. syntactic correctness and copying files if directed to do so.
  578. Arguments:
  579. Flags - Supplies flags controlling various behavior:
  580. CPY_VALIDATION_PASS: if set, do not actually copy the files.
  581. If not set, copy the files as they are iterated.
  582. CPY_USE_DEST_ROOT - if set, ignore the directory symbol and copy
  583. each file to the DestinationRoot directory. If not set,
  584. append the directory implied by the directory symbol for a file
  585. to the DestinationRoot.
  586. CPY_VERIFY - if set and this is not a validation pass, files will be
  587. verified after they have been copied by rereading them from the
  588. copy source and comparing with the local version that was just
  589. copied.
  590. SectionName - name of section coptaining the list of files
  591. DestinationRoot- supplies the root of the destination, to which all
  592. directories are relative.
  593. ClusterSize - if specified, supplies the number of bytes in a cluster
  594. on the destination. If ValidationPass is FALSE, files will be sized as
  595. they are copied, and the return value of this function will be
  596. the total size occupied on the target by the files that are copied
  597. there.
  598. Return Value:
  599. If ValidationPass is TRUE, then the return value is the number of files
  600. that will be copied.
  601. If ClusterSize was specfied and ValidationPass is FALSE,
  602. the return value is the total space occupied on the target drive
  603. by the files that were copied. Otherwise the return value is undefined.
  604. Does not return if a syntax error in encountered in the INF file.
  605. --*/
  606. {
  607. if(Flags & CPY_VALIDATION_PASS) {
  608. TotalFileCount = 0;
  609. } else {
  610. if(UsingGauge) {
  611. DnInitGauge(TotalFileCount,CopyingScreen);
  612. }
  613. }
  614. return(DnpIterateCopyListSection(Flags,SectionName,DestinationRoot,ClusterSize));
  615. }
  616. ULONG
  617. DnpIterateOptionalDirs(
  618. IN unsigned Flags,
  619. IN unsigned ClusterSize OPTIONAL,
  620. IN PSPACE_REQUIREMENT SpaceReqArray OPTIONAL,
  621. IN unsigned ArraySize OPTIONAL
  622. )
  623. /*++
  624. Routine Description:
  625. Runs down all optional dir components and add them to the copy
  626. list
  627. Arguments:
  628. Flags - supplies flags controlling various behavior:
  629. CPY_VALIDATION_PASS - If set, then do not actually copy the files.
  630. If not set, copy the files as they are iterated.
  631. CPY_VERIFY: if set and this is not a validation pass, files will be
  632. verified after they have been copied by rereading them from the
  633. copy source and comparing with the local version that was just
  634. copied.
  635. ClusterSize - if specified, supplies the number of bytes in a cluster
  636. on the destination. If ValidationPass is FALSE, files will be sized as
  637. they are copied, and the return value of this function will be
  638. the total size occupied on the target by the files that are copied
  639. there.
  640. Return Value:
  641. If CPY_VALIDATION_PASS is set, then the return value is the number of files
  642. that will be copied.
  643. If ClusterSize was specfied and CPY_VALIDATION_PASS is not set,
  644. the return value is the total space occupied on the target drive
  645. by the files that were copied. Otherwise the return value is undefined.
  646. --*/
  647. {
  648. PCHAR Ptr;
  649. PCHAR SourceDir;
  650. PCHAR DestDir;
  651. ULONG rc;
  652. unsigned u;
  653. BOOLEAN OemOptDirCreated = FALSE;
  654. struct find_t FindData;
  655. BOOLEAN OemSysDirExists;
  656. #if 0
  657. //
  658. // Debugging purposes
  659. //
  660. SaveTotalOptionalDirCount = TotalOptionalDirCount;
  661. #endif
  662. TotalOptionalDirCount = 0;
  663. for (rc=0,u=0; u < OptionalDirCount; u++ ) {
  664. //
  665. // For each directory build in the list build up the
  666. // full path name to both the source and destination
  667. // directory, then start our recursive copy engine
  668. //
  669. //
  670. // Source Dir Allocation
  671. // We want the base dir + '\'
  672. // + oem optional dir root + '\'
  673. // + optional dir name + '\'
  674. // + 8.3 name + '\0'
  675. //
  676. if( (OptionalDirFlags[u] & OPTDIR_OEMSYS) &&
  677. (UserSpecifiedOEMShare ) ) {
  678. SourceDir = MALLOC( strlen(UserSpecifiedOEMShare) +
  679. strlen(OptionalDirs[u]) + 16, TRUE );
  680. strcpy(SourceDir,UserSpecifiedOEMShare);
  681. if( SourceDir[strlen(SourceDir)-1] != '\\' ) {
  682. strcat(SourceDir,"\\");
  683. }
  684. } else {
  685. SourceDir = MALLOC( strlen(DngSourceRootPath) +
  686. strlen(OptionalDirs[u]) + 16, TRUE );
  687. strcpy(SourceDir,DngSourceRootPath);
  688. if ((OptionalDirFlags[u] & OPTDIR_PLATFORM_INDEP) == 0) {
  689. strcat(SourceDir,"\\");
  690. } else {
  691. PCHAR LastBackslash = strchr(SourceDir, '\\');
  692. if (LastBackslash != NULL) {
  693. *(LastBackslash + 1) = 0;
  694. }
  695. }
  696. }
  697. #if 0
  698. // No longer supported (matth)
  699. if (OptionalDirFlags[u] & OPTDIR_OEMOPT) {
  700. strcat(SourceDir,OemOptionalDirectory);
  701. strcat(SourceDir,"\\");
  702. }
  703. #endif
  704. strcat(SourceDir,OptionalDirs[u]);
  705. if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
  706. //
  707. // Remember whether or not $OEM$ exists on the source
  708. //
  709. if (_dos_findfirst(SourceDir,_A_HIDDEN|_A_SYSTEM|_A_SUBDIR, &FindData) ) {
  710. OemSysDirExists = FALSE;
  711. } else {
  712. OemSysDirExists = TRUE;
  713. }
  714. }
  715. strcat(SourceDir,"\\");
  716. //
  717. // Dest Dir Allocation
  718. // This depends if the 'SourceOnly' flag is set with the directory
  719. // If it is, then we want to copy it $win_nt$.~ls\i386\<dir> otherwise
  720. // we want to stick into $win_nt$.~ls\<dir>
  721. //
  722. if ((OptionalDirFlags[u] & OPTDIR_TEMPONLY) && !(OptionalDirFlags[u] & OPTDIR_PLATFORM_INDEP)) {
  723. //
  724. // Dest Dir is '<x>:' + LocalSourceDirName + x86dir + '\' +
  725. // optional dir name + '\' + 8.3 name + '\0'
  726. //
  727. DestDir = MALLOC(strlen(LocalSourceDirName) +
  728. strlen(x86DirName) +strlen(OptionalDirs[u]) + 17, TRUE);
  729. DestDir[0] = DngTargetDriveLetter;
  730. DestDir[1] = ':';
  731. strcpy(DestDir+2,LocalSourceDirName);
  732. strcat(DestDir,x86DirName);
  733. } else if (OptionalDirFlags[u] & OPTDIR_OEMOPT) {
  734. //
  735. // Dest Dir is '<x>:' + LocalSourceDirName + '\' +
  736. // $OEMOPT$ + '\' +
  737. // optional dir name + '\' + 8.3 name + '\0'
  738. //
  739. DestDir = MALLOC(strlen(LocalSourceDirName) +
  740. strlen(OemOptionalDirectory) +
  741. strlen(OptionalDirs[u]) + 18, TRUE);
  742. DestDir[0] = DngTargetDriveLetter;
  743. DestDir[1] = ':';
  744. strcpy(DestDir+2,LocalSourceDirName);
  745. strcat(DestDir,"\\");
  746. strcat(DestDir,OemOptionalDirectory);
  747. if (!(Flags & CPY_VALIDATION_PASS) && !OemOptDirCreated) {
  748. DnpCreateOneDirectory(DestDir);
  749. OemOptDirCreated = TRUE;
  750. TotalOptionalDirCount++;
  751. }
  752. } else if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
  753. //
  754. // Dest Dir is '<x>:' + '\' + '$' + '\' + 8.3 name + '\0'
  755. //
  756. // Note that on winnt case the directory $OEM$ goes to
  757. // <drive letter>\$ directory. This is to avoid hitting the
  758. // DOS limit of 64 characters on a path, that is more likely to
  759. // happen if we put $OEM$ under \$win_nt$.~ls
  760. //
  761. DestDir = MALLOC(strlen( WINNT_OEM_DEST_DIR ) + 17, TRUE);
  762. DestDir[0] = DngTargetDriveLetter;
  763. DestDir[1] = ':';
  764. DestDir[2] = '\0';
  765. } else {
  766. //
  767. // Dest Dir is '<x>:' + LocalSourceDirName + '\' +
  768. // optional dir name + '\' + 8.3 name + '\0'
  769. //
  770. DestDir = MALLOC(strlen(LocalSourceDirName) +
  771. strlen(OptionalDirs[u]) + 17, TRUE);
  772. DestDir[0] = DngTargetDriveLetter;
  773. DestDir[1] = ':';
  774. strcpy(DestDir+2,LocalSourceDirName);
  775. }
  776. //
  777. // We need a trailing backslash at this point
  778. //
  779. strcat(DestDir,"\\");
  780. //
  781. // Keep a pointer to the place we the optional dir part of
  782. // the string begins
  783. //
  784. Ptr = DestDir + strlen(DestDir);
  785. //
  786. // Add the optional dir name
  787. //
  788. if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
  789. strcat(DestDir,WINNT_OEM_DEST_DIR);
  790. } else {
  791. strcat(DestDir,OptionalDirs[u]);
  792. }
  793. if (!(Flags & CPY_VALIDATION_PASS)) {
  794. //
  795. // Create the Directory now
  796. //
  797. while (*Ptr != '\0') {
  798. //
  799. // If the current pointer is a backslash then we need to
  800. // create this subcomponent of the optional dir
  801. //
  802. if (*Ptr == '\\') {
  803. //
  804. // Replace the char with a terminator for now
  805. //
  806. *Ptr = '\0';
  807. //
  808. // Create the subdirectory
  809. //
  810. DnpCreateOneDirectory(DestDir);
  811. TotalOptionalDirCount++;
  812. //
  813. // Restore the seperator
  814. //
  815. *Ptr = '\\';
  816. }
  817. Ptr++;
  818. }
  819. //
  820. // Create the last component in the optional dir path
  821. //
  822. DnpCreateOneDirectory(DestDir);
  823. TotalOptionalDirCount++;
  824. } else {
  825. TotalOptionalDirCount++;
  826. }
  827. //
  828. // Concate the trailing backslash now
  829. //
  830. strcat(DestDir,"\\");
  831. //
  832. // If the the optional directory is $OEM$ and it doesn't exist on
  833. // source, then assume that it exists, but it is empty
  834. //
  835. if ( !(OptionalDirFlags[u] & OPTDIR_OEMSYS) ||
  836. OemSysDirExists ) {
  837. //
  838. // Call our recursive tree copy function
  839. //
  840. rc += DnpDoIterateOptionalDir(
  841. Flags,
  842. SourceDir,
  843. DestDir,
  844. ClusterSize,
  845. SpaceReqArray,
  846. ArraySize
  847. );
  848. }
  849. //
  850. // Free the allocated buffers
  851. //
  852. FREE(DestDir);
  853. FREE(SourceDir);
  854. } // for
  855. //
  856. // return our result code if we aren't a validation pass, otherwise
  857. // return the total number of files to copy
  858. //
  859. return ((Flags & CPY_VALIDATION_PASS) ? (ULONG) TotalOptionalFileCount : rc);
  860. }
  861. ULONG
  862. DnpDoIterateOptionalDir(
  863. IN unsigned Flags,
  864. IN PCHAR SourceDir,
  865. IN PCHAR DestDir,
  866. IN unsigned ClusterSize OPTIONAL,
  867. IN PSPACE_REQUIREMENT SpaceReqArray OPTIONAL,
  868. IN unsigned ArraySize OPTIONAL
  869. )
  870. {
  871. ULONG TotalSize = 0;
  872. ULONG BytesWritten = 0;
  873. ULONG rc = 0;
  874. PCHAR SourceEnd;
  875. PCHAR DestEnd;
  876. struct find_t FindData;
  877. unsigned i;
  878. //
  879. // Remember where the last '\' in each of the two paths is.
  880. // Note: that we assume that all of the dir paths have a
  881. // terminating '\' when it is passed to us.
  882. //
  883. SourceEnd = SourceDir + strlen(SourceDir);
  884. DestEnd = DestDir + strlen(DestDir);
  885. //
  886. // Set the WildCard search string
  887. //
  888. strcpy(SourceEnd,"*.*");
  889. //
  890. // Do the initial search
  891. //
  892. if(_dos_findfirst(SourceDir,_A_HIDDEN|_A_SYSTEM|_A_SUBDIR, &FindData) ) {
  893. //
  894. // We couldn't find anything -- return 0
  895. //
  896. return (0);
  897. }
  898. do {
  899. //
  900. // Form the source and dest dirs strings
  901. //
  902. strcpy(SourceEnd,FindData.name);
  903. strcpy(DestEnd,FindData.name);
  904. //
  905. // Check to see if the entry is a subdir. Recurse into it
  906. // unless it is '.' or '..'
  907. //
  908. if (FindData.attrib & _A_SUBDIR) {
  909. PCHAR NewSource;
  910. PCHAR NewDest;
  911. //
  912. // Check to see if the name is '.' or '..'
  913. //
  914. if (!strcmp(FindData.name,".") || !strcmp(FindData.name,"..")) {
  915. //
  916. // Ignore these two cases
  917. //
  918. continue;
  919. }
  920. //
  921. // Create the new buffers for the source and dest dir names
  922. //
  923. NewSource = MALLOC( strlen(SourceDir) + 14, TRUE);
  924. strcpy(NewSource,SourceDir);
  925. if (NewSource[strlen(NewSource)-1] != '\\') {
  926. strcat(NewSource,"\\");
  927. }
  928. NewDest = MALLOC( strlen(DestDir) + 14, TRUE);
  929. strcpy(NewDest,DestDir);
  930. if(!(Flags & CPY_VALIDATION_PASS)) {
  931. //
  932. // Create the directory
  933. //
  934. DnpCreateOneDirectory(NewDest);
  935. }
  936. TotalOptionalDirCount++;
  937. //
  938. // Trailing BackSlash
  939. //
  940. if (NewDest[strlen(NewDest)-1] != '\\') {
  941. strcat(NewDest,"\\");
  942. }
  943. //
  944. // Recursive call to ourselves
  945. //
  946. BytesWritten = DnpDoIterateOptionalDir(
  947. Flags,
  948. NewSource,
  949. NewDest,
  950. ClusterSize,
  951. SpaceReqArray,
  952. ArraySize
  953. );
  954. if(!(Flags & CPY_VALIDATION_PASS)) {
  955. //
  956. // We don't care about the other case since the
  957. // function is recursive and modifies a global
  958. // value
  959. //
  960. rc += BytesWritten;
  961. }
  962. //
  963. // Free all of the allocated buffers
  964. //
  965. FREE(NewSource);
  966. FREE(NewDest);
  967. //
  968. // Continue Processing
  969. //
  970. continue;
  971. } // if ...
  972. //
  973. // Mainline case
  974. //
  975. if(Flags & CPY_VALIDATION_PASS) {
  976. TotalOptionalFileCount++;
  977. if( SpaceReqArray != NULL ) {
  978. for( i = 0; i < ArraySize; i++ ) {
  979. SpaceReqArray[i].Clusters += ( FindData.size + ( SpaceReqArray[i].ClusterSize - 1 ) ) / SpaceReqArray[i].ClusterSize;
  980. }
  981. }
  982. } else {
  983. BytesWritten = DnpCopyOneFile(
  984. Flags | CPY_PRESERVE_ATTRIBS,
  985. SourceDir,
  986. DestDir
  987. );
  988. //
  989. // Figure out how much space was taken up by the file on the target.
  990. //
  991. if(ClusterSize) {
  992. TotalSize += BytesWritten;
  993. if(BytesWritten % ClusterSize) {
  994. TotalSize += (ULONG)ClusterSize - (BytesWritten % ClusterSize);
  995. }
  996. }
  997. if(UsingGauge) {
  998. DnTickGauge();
  999. }
  1000. }
  1001. } while ( !_dos_findnext(&FindData) );
  1002. DnSetCopyStatusText(DntEmptyString,NULL);
  1003. rc = ((Flags & CPY_VALIDATION_PASS) ? (ULONG)TotalOptionalFileCount : (rc + TotalSize));
  1004. return (rc);
  1005. }
  1006. ULONG
  1007. DnpIterateCopyListSection(
  1008. IN unsigned Flags,
  1009. IN PCHAR SectionName,
  1010. IN PCHAR DestinationRoot,
  1011. IN unsigned ClusterSize OPTIONAL
  1012. )
  1013. /*++
  1014. Routine Description:
  1015. Run down a particular section in the INF file making sure it is
  1016. syntactically correct and copying files if directed to do so.
  1017. Arguments:
  1018. Flags - Supplies flags controlling various behavior:
  1019. CPY_VALIDATION_PASS: if set, do not actually copy the files.
  1020. If not set, copy the files as they are iterated.
  1021. CPY_USE_DEST_ROOT - if set, ignore the directory symbol and copy
  1022. each file to the DestinationRoot directory. If not set,
  1023. append the directory implied by the directory symbol for a file
  1024. to the DestinationRoot.
  1025. CPY_VERIFY - if set and this is not a validation pass, files will be
  1026. verified after they have been copied by rereading them from the
  1027. copy source and comparing with the local version that was just
  1028. copied.
  1029. SectionName - supplies name of section in inf file to run down.
  1030. DestinationRoot- supplies the root of the destination, to which all
  1031. directories are relative.
  1032. ClusterSize - if specified, supplies the number of bytes in a cluster
  1033. on the destination. If ValidationPass is FALSE, files will be sized as
  1034. they are copied, and the return value of this function will be
  1035. the total size occupied on the target by the files that are copied
  1036. there.
  1037. Return Value:
  1038. If ValidationPass is TRUE, then the return value is the number of files
  1039. that will be copied.
  1040. If ClusterSize was specfied and ValidationPass is FALSE,
  1041. the return value is the total space occupied on the target drive
  1042. by the files that were copied. Otherwise the return value is undefined.
  1043. Does not return if a syntax error in encountered in the INF file.
  1044. --*/
  1045. {
  1046. unsigned LineIndex;
  1047. PCHAR DirSym,FileName,RenameName;
  1048. CHAR FullSourceName[128],FullDestName[128];
  1049. ULONG TotalSize;
  1050. ULONG BytesWritten;
  1051. char *p;
  1052. char *q;
  1053. TotalSize = 0;
  1054. LineIndex = 0;
  1055. while(DirSym = DnGetSectionLineIndex(DngInfHandle,SectionName,LineIndex,0)) {
  1056. if ( DngCopyOnlyD1TaggedFiles )
  1057. {
  1058. // We skip this line if the directory tag is not 'd1'
  1059. if ( strcmpi(DirSym, "d1") )
  1060. goto loop_continue;
  1061. }
  1062. FileName = DnGetSectionLineIndex(DngInfHandle,SectionName,LineIndex,1);
  1063. RenameName = DnGetSectionLineIndex( DngInfHandle,SectionName,LineIndex,2);
  1064. //
  1065. // Make sure the filename was specified.
  1066. //
  1067. if(!FileName) {
  1068. DnpInfSyntaxError(SectionName);
  1069. }
  1070. //_LOG(("File %s - Flags %x\n",FileName, Flags));
  1071. if( Flags & CPY_PRUNE_DRIVERCAB ){
  1072. if( InDriverCacheInf( DngDrvindexInfHandle, FileName )) {
  1073. //_LOG(("%s present in driver cab\n",FileName));
  1074. if( !DnGetSectionEntryExists( DngInfHandle, WINNT_D_FORCECOPYDRIVERCABFILES, FileName)){
  1075. //_LOG(("%s present in driver cab - skipping\n",FileName));
  1076. goto next_iteration;
  1077. }
  1078. }
  1079. }
  1080. //
  1081. // If no rename name was specified, use the file name.
  1082. //
  1083. if (!RenameName) {
  1084. RenameName = FileName;
  1085. }
  1086. if (*RenameName == 0) {
  1087. FREE (RenameName);
  1088. RenameName = FileName;
  1089. }
  1090. //
  1091. // Get destination path
  1092. //
  1093. if(Flags & CPY_USE_DEST_ROOT) {
  1094. strcpy(FullDestName,DestinationRoot);
  1095. } else {
  1096. if(!DnpLookUpDirectory(DestinationRoot,DirectoryList,DirSym,FullDestName)) {
  1097. DnpInfSyntaxError(SectionName);
  1098. }
  1099. }
  1100. p = strstr( FullDestName, x86DirName );
  1101. if (p) {
  1102. p +=1;
  1103. // 2 CD Setup changes - instead of getting rid of the second i386 we get rid of the FIRST i386
  1104. #if 0
  1105. p = strstr(p, x86DirName );
  1106. if (p) {
  1107. *p = (char)NULL;
  1108. }
  1109. #else
  1110. if ( strstr(p, x86DirName) )
  1111. {
  1112. for ( q = p + strlen(x86DirName); *q ; q++, p++ )
  1113. {
  1114. *p = *q;
  1115. }
  1116. *p = (char)NULL;
  1117. }
  1118. #endif
  1119. }
  1120. DnpConcatPaths(FullDestName,RenameName);
  1121. //
  1122. // Get source path
  1123. //
  1124. if(!DnpLookUpDirectory(DngSourceRootPath,DirectoryList,DirSym,FullSourceName)) {
  1125. DnpInfSyntaxError(SectionName);
  1126. }
  1127. p = strstr( FullSourceName, x86DirName );
  1128. if (p) {
  1129. p +=1;
  1130. // 2 CD Setup changes - instead of getting rid of the second i386 we get rid of the FIRST i386
  1131. #if 0
  1132. p = strstr(p, x86DirName );
  1133. if (p) {
  1134. *p = (char)NULL;
  1135. }
  1136. #else
  1137. if ( strstr(p, x86DirName) )
  1138. {
  1139. for ( q = p + strlen(x86DirName) ; *q ; q++, p++ )
  1140. {
  1141. *p = *q;
  1142. }
  1143. *p = (char)NULL;
  1144. }
  1145. #endif
  1146. }
  1147. DnpConcatPaths(FullSourceName,FileName);
  1148. if(Flags & CPY_VALIDATION_PASS) {
  1149. TotalFileCount++;
  1150. } else {
  1151. BytesWritten = DnpCopyOneFile(
  1152. Flags & ~CPY_PRESERVE_ATTRIBS,
  1153. FullSourceName,
  1154. FullDestName
  1155. );
  1156. //
  1157. // Figure out how much space was taken up by the file on the target.
  1158. //
  1159. if(ClusterSize) {
  1160. TotalSize += BytesWritten;
  1161. if(BytesWritten % ClusterSize) {
  1162. TotalSize += (ULONG)ClusterSize - (BytesWritten % ClusterSize);
  1163. }
  1164. }
  1165. if(UsingGauge) {
  1166. DnTickGauge();
  1167. }
  1168. }
  1169. next_iteration:
  1170. if (RenameName != FileName) {
  1171. FREE (RenameName);
  1172. }
  1173. if (FileName) {
  1174. FREE (FileName);
  1175. }
  1176. loop_continue:
  1177. LineIndex++;
  1178. FREE (DirSym);
  1179. }
  1180. DnSetCopyStatusText(DntEmptyString,NULL);
  1181. return((Flags & CPY_VALIDATION_PASS) ? (ULONG)TotalFileCount : TotalSize);
  1182. }
  1183. BOOLEAN
  1184. DnpLookUpDirectory(
  1185. IN PCHAR RootDirectory,
  1186. IN PDIRECTORY_NODE DirList,
  1187. IN PCHAR Symbol,
  1188. OUT PCHAR PathOut
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. Match a symbol to an actual directory. Scans a give list of symbol/
  1193. directory pairs and if a match is found constructs a fully qualified
  1194. pathname that never ends in '\'.
  1195. Arguments:
  1196. RootDirectory - supplies the beginning of the path spec, to be prepended
  1197. to the directory that matches the given Symbol.
  1198. DirList - supplies pointer to head of linked list of dir/symbol pairs.
  1199. Symbol - Symbol to match.
  1200. PathOut - supplies a pointer to a buffer that receives the pathname.
  1201. Return Value:
  1202. Boolean value indicating whether a match was found.
  1203. --*/
  1204. {
  1205. while(DirList) {
  1206. if(!stricmp(DirList->Symbol,Symbol)) {
  1207. strcpy(PathOut,RootDirectory);
  1208. if(*DirList->Directory) {
  1209. // 2 CD setup changes - all directories now start with \ anyway,
  1210. // so we don't need to append this.
  1211. // make sure the current path doesn't end in a '\'
  1212. if ( PathOut[strlen(PathOut)-1] == '\\')
  1213. PathOut[strlen(PathOut)-1] = '0';
  1214. strcat(PathOut,DirList->Directory);
  1215. }
  1216. return(TRUE);
  1217. }
  1218. DirList = DirList->Next;
  1219. }
  1220. return(FALSE);
  1221. }
  1222. VOID
  1223. DnpInfSyntaxError(
  1224. IN PCHAR Section
  1225. )
  1226. /*++
  1227. Routine Description:
  1228. Print an error message about a syntax error in the given section and
  1229. terminate.
  1230. Arguments:
  1231. SectionName - supplies name of section containing bad syntax.
  1232. Return Value:
  1233. None. Does not return.
  1234. --*/
  1235. {
  1236. CHAR MsgLine1[128];
  1237. snprintf(MsgLine1,sizeof(MsgLine1),DnsBadInfSection.Strings[BAD_SECTION_LINE],Section);
  1238. DnsBadInfSection.Strings[BAD_SECTION_LINE] = MsgLine1;
  1239. DnFatalError(&DnsBadInfSection);
  1240. }
  1241. ULONG
  1242. DnpCopyOneFile(
  1243. IN unsigned Flags,
  1244. IN PCHAR SourceName,
  1245. IN PCHAR DestName
  1246. )
  1247. /*++
  1248. Routine Description:
  1249. Copies a single file.
  1250. Arguments:
  1251. Flags - supplies flags that control various behavior:
  1252. CPY_VERIFY: verify the file will be after it has been copied.
  1253. CPY_PRESERVE_ATTRIBS: preserve the DOS file attributes of
  1254. the source file.
  1255. SourceName - supplies fully qualified name of source file
  1256. DestName - supplies fully qualified name of destination file
  1257. Return Value:
  1258. None. May not return if an error occurs during the copy.
  1259. --*/
  1260. {
  1261. int SrcHandle,DstHandle;
  1262. BOOLEAN Err,Retry;
  1263. PCHAR FilenamePart;
  1264. BOOLEAN Verified;
  1265. ULONG BytesWritten = 0;
  1266. unsigned attribs;
  1267. BOOLEAN UsedCompName;
  1268. CHAR ActualDestName[128];
  1269. FilenamePart = strrchr(SourceName,'\\') + 1;
  1270. do {
  1271. DnSetCopyStatusText(DntCopying,FilenamePart);
  1272. Err = TRUE;
  1273. //_LOG(("Copy %s --> %s: ",SourceName,DestName));
  1274. if(DnpOpenSourceFile(SourceName,&SrcHandle,&attribs,&UsedCompName)) {
  1275. if((Flags & CPY_PRESERVE_NAME) && UsedCompName) {
  1276. DnpGenerateCompressedName(DestName,ActualDestName);
  1277. } else {
  1278. strcpy(ActualDestName,DestName);
  1279. }
  1280. _dos_setfileattr(ActualDestName,_A_NORMAL);
  1281. if(!_dos_creat(ActualDestName,_A_NORMAL,&DstHandle)) {
  1282. if(DnpDoCopyOneFile(Flags,SrcHandle,DstHandle,FilenamePart,&Verified,&BytesWritten)) {
  1283. //_LOG(("success\n"));
  1284. Err = FALSE;
  1285. }
  1286. _dos_close(DstHandle);
  1287. } else {
  1288. //_LOG(("unable to create target\n"));
  1289. }
  1290. _dos_close(SrcHandle);
  1291. } else {
  1292. //_LOG(("unable to open source file\n"));
  1293. }
  1294. if((Flags & CPY_PRESERVE_ATTRIBS) && (attribs & (_A_HIDDEN | _A_RDONLY | _A_SYSTEM)) && !Err) {
  1295. _dos_setfileattr(ActualDestName,attribs);
  1296. }
  1297. if(Err) {
  1298. Retry = DnCopyError(FilenamePart,&DnsCopyError,COPYERR_LINE);
  1299. if(UsingGauge) {
  1300. DnDrawGauge(CopyingScreen);
  1301. } else {
  1302. DnClearClientArea();
  1303. DnDisplayScreen(CopyingScreen);
  1304. }
  1305. DnWriteStatusText(NULL);
  1306. } else if((Flags & CPY_VERIFY) && !Verified) {
  1307. Retry = DnCopyError(FilenamePart,&DnsVerifyError,VERIFYERR_LINE);
  1308. if(UsingGauge) {
  1309. DnDrawGauge(CopyingScreen);
  1310. } else {
  1311. DnClearClientArea();
  1312. DnDisplayScreen(CopyingScreen);
  1313. }
  1314. DnWriteStatusText(NULL);
  1315. Err = TRUE;
  1316. }
  1317. } while(Err && Retry);
  1318. return(BytesWritten);
  1319. }
  1320. BOOLEAN
  1321. DnpDoCopyOneFile(
  1322. IN unsigned Flags,
  1323. IN int SrcHandle,
  1324. IN int DstHandle,
  1325. IN PCHAR Filename,
  1326. OUT PBOOLEAN Verified,
  1327. OUT PULONG BytesWritten
  1328. )
  1329. /*++
  1330. Routine Description:
  1331. Perform the actual copy of a single file.
  1332. Arguments:
  1333. Flags - supplies various flags controlling behavior of this routine:
  1334. CPY_VERIFY: if set, the copied file will be verified against the
  1335. original copy.
  1336. SrcHandle - supplies the DOS file handle for the open source file.
  1337. DstHandle - supplies the DOS file handle for the open target file.
  1338. Filename - supplies the base filename of the file being copied.
  1339. This is used in the status bar at the bottom of the screen.
  1340. Verified - if CPY_VERIFY is set and the copy succeeds, this value will
  1341. receive a flag indicating whether the file verification
  1342. determined that the file was copied correctly.
  1343. BytesWritten - Receives the number of bytes written to
  1344. the target file (ie, the file size).
  1345. Return Value:
  1346. TRUE if the copy succeeded, FALSE if it failed for any reason.
  1347. If TRUE and CPY_VERIFY is set, the caller should also check the value
  1348. returned in the Verified variable.
  1349. --*/
  1350. {
  1351. unsigned BytesRead,bytesWritten;
  1352. BOOLEAN TimestampValid;
  1353. unsigned Date,Time;
  1354. PUCHAR VerifyBuffer;
  1355. //
  1356. // Assume verification will succeed. If the file is not copied correctly,
  1357. // this value will become irrelevent.
  1358. //
  1359. if(Verified) {
  1360. *Verified = TRUE;
  1361. }
  1362. //
  1363. // If the copy buffer is not already allocated, attempt to allocate it.
  1364. // The first two attempts can fail because we have a fallback size to try.
  1365. // If the third attempt fails, bail.
  1366. //
  1367. if((CopyBuffer == NULL)
  1368. &&((CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE1,FALSE)) == NULL)
  1369. &&((CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE2,FALSE)) == NULL)) {
  1370. CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE3,TRUE);
  1371. }
  1372. //
  1373. // Obtain the timestamp from the source file.
  1374. //
  1375. TimestampValid = (BOOLEAN)(_dos_getftime(SrcHandle,&Date,&Time) == 0);
  1376. //
  1377. // read and write chunks of the file.
  1378. //
  1379. *BytesWritten = 0L;
  1380. do {
  1381. if(_dos_read(SrcHandle,CopyBuffer,CopyBufferSize,&BytesRead)) {
  1382. //_LOG(("read error\n"));
  1383. return(FALSE);
  1384. }
  1385. if(BytesRead) {
  1386. if(_dos_write(DstHandle,CopyBuffer,BytesRead,&bytesWritten)
  1387. || (BytesRead != bytesWritten))
  1388. {
  1389. //_LOG(("write error\n"));
  1390. return(FALSE);
  1391. }
  1392. *BytesWritten += bytesWritten;
  1393. }
  1394. } while(BytesRead == CopyBufferSize);
  1395. //
  1396. // Perserve the original timestamp.
  1397. //
  1398. if(TimestampValid) {
  1399. _dos_setftime(DstHandle,Date,Time);
  1400. }
  1401. if(Flags & CPY_VERIFY) {
  1402. union REGS RegsIn,RegsOut;
  1403. DnSetCopyStatusText(DntVerifying,Filename);
  1404. *Verified = FALSE; // assume failure
  1405. //
  1406. // Rewind the files.
  1407. //
  1408. RegsIn.x.ax = 0x4200; // seek to offset from start of file
  1409. RegsIn.x.bx = SrcHandle;
  1410. RegsIn.x.cx = 0; // offset = 0
  1411. RegsIn.x.dx = 0;
  1412. intdos(&RegsIn,&RegsOut);
  1413. if(RegsOut.x.cflag) {
  1414. goto x1;
  1415. }
  1416. RegsIn.x.bx = DstHandle;
  1417. intdos(&RegsIn,&RegsOut);
  1418. if(RegsOut.x.cflag) {
  1419. goto x1;
  1420. }
  1421. //
  1422. // Files are rewound. Start the verification process.
  1423. // Use half the buffer for reading the copy and the other half
  1424. // to read the original.
  1425. //
  1426. VerifyBuffer = (PUCHAR)CopyBuffer + (CopyBufferSize/2);
  1427. do {
  1428. if(_dos_read(SrcHandle,CopyBuffer,CopyBufferSize/2,&BytesRead)) {
  1429. goto x1;
  1430. }
  1431. if(_dos_read(DstHandle,VerifyBuffer,CopyBufferSize/2,&bytesWritten)) {
  1432. goto x1;
  1433. }
  1434. if(BytesRead != bytesWritten) {
  1435. goto x1;
  1436. }
  1437. if(memcmp(CopyBuffer,VerifyBuffer,BytesRead)) {
  1438. goto x1;
  1439. }
  1440. } while(BytesRead == CopyBufferSize/2);
  1441. *Verified = TRUE;
  1442. }
  1443. x1:
  1444. return(TRUE);
  1445. }
  1446. VOID
  1447. DnpFreeDirectoryList(
  1448. IN OUT PDIRECTORY_NODE *List
  1449. )
  1450. /*++
  1451. Routine Description:
  1452. Free a linked list of directory nodes and place NULL in the
  1453. head pointer.
  1454. Arguments:
  1455. List - supplies pointer to list head pointer; receives NULL.
  1456. Return Value:
  1457. None.
  1458. --*/
  1459. {
  1460. PDIRECTORY_NODE n,p = *List;
  1461. while(p) {
  1462. n = p->Next;
  1463. FREE(p->Directory);
  1464. FREE(p->Symbol);
  1465. FREE(p);
  1466. p = n;
  1467. }
  1468. *List = NULL;
  1469. }
  1470. VOID
  1471. DnDetermineSpaceRequirements(
  1472. PSPACE_REQUIREMENT SpaceReqArray,
  1473. unsigned ArraySize
  1474. )
  1475. /*++
  1476. Routine Description:
  1477. Read space requirements from the inf file, and initialize SpaceReqArray.
  1478. The 'space requirement' is the amount of free disk space for all files
  1479. listed on dosnet.inf. The size of the files in the optional directories
  1480. are not included in the values specified on dosnet.inf.
  1481. Arguments:
  1482. RequiredSpace - receives the number of bytes of free space on a drive
  1483. for it to be a valid local source.
  1484. Return Value:
  1485. None.
  1486. --*/
  1487. {
  1488. PCHAR RequiredSpaceStr;
  1489. unsigned i;
  1490. for( i = 0; i < ArraySize; i++ ) {
  1491. RequiredSpaceStr = DnGetSectionKeyIndex( DngInfHandle,
  1492. DnfSpaceRequirements,
  1493. SpaceReqArray[i].Key,
  1494. 0
  1495. );
  1496. if(!RequiredSpaceStr ||
  1497. !sscanf(RequiredSpaceStr,
  1498. "%lu",
  1499. &SpaceReqArray[i].Clusters)) {
  1500. DnpInfSyntaxError(DnfSpaceRequirements);
  1501. }
  1502. SpaceReqArray[i].Clusters /= SpaceReqArray[i].ClusterSize;
  1503. if (RequiredSpaceStr) {
  1504. FREE (RequiredSpaceStr);
  1505. }
  1506. }
  1507. }
  1508. VOID
  1509. DnAdjustSpaceRequirements(
  1510. PSPACE_REQUIREMENT SpaceReqArray,
  1511. unsigned ArraySize
  1512. )
  1513. /*++
  1514. Routine Description:
  1515. Add to the SpaceRequirements array the space occupied by the temporary
  1516. directories
  1517. Arguments:
  1518. SpaceReqArray - receives the array that contains the space requirements
  1519. information.
  1520. ArraySize - Number of elements in the SpaceRequirements Array
  1521. Return Value:
  1522. None.
  1523. --*/
  1524. {
  1525. unsigned i;
  1526. unsigned ClusterSize;
  1527. unsigned AvFilesPerOptionalDir;
  1528. for( i = 0; i < ArraySize; i++ ) {
  1529. ClusterSize = SpaceReqArray[i].ClusterSize;
  1530. //
  1531. // Takes into consideration the dirent for each file in the flat directory
  1532. // plust the directories . and .., plus size.sif, plus $win_nt_.~ls and
  1533. // $win_nt$.~ls\i386
  1534. //
  1535. SpaceReqArray[i].Clusters += (((TotalFileCount - TotalOptionalFileCount) + // number of files in the $win_nt$.~ls\i386 directory
  1536. 1 + // $win_nt$.~ls
  1537. 2 + // . and .. on $win_nt$.~ls
  1538. 1 + // size.sif on $win_nt$.~ls
  1539. 1 + // $win_nt$.~ls\i386
  1540. 2 // . and .. on $win_nt$.~ls\i386
  1541. )*32 + (ClusterSize-1)) / ClusterSize;
  1542. //
  1543. // Now take into consideration the optional directories.
  1544. //
  1545. if(TotalOptionalDirCount != 0) {
  1546. //
  1547. // We assume a uniform distribution of optional files on optional
  1548. // directories
  1549. //
  1550. AvFilesPerOptionalDir = (TotalOptionalFileCount + (TotalOptionalDirCount - 1))/TotalOptionalDirCount;
  1551. AvFilesPerOptionalDir += 2; // . and .. on each optional dir
  1552. SpaceReqArray[i].Clusters += TotalOptionalDirCount*((AvFilesPerOptionalDir*32 + (ClusterSize-1))/ClusterSize);
  1553. //
  1554. // Finally take into account each optional directory
  1555. //
  1556. SpaceReqArray[i].Clusters += (TotalOptionalDirCount*32 + (ClusterSize-1))/ClusterSize;
  1557. }
  1558. }
  1559. }
  1560. VOID
  1561. DnpGenerateCompressedName(
  1562. IN PCHAR Filename,
  1563. OUT PCHAR CompressedName
  1564. )
  1565. /*++
  1566. Routine Description:
  1567. Given a filename, generate the compressed form of the name.
  1568. The compressed form is generated as follows:
  1569. Look backwards for a dot. If there is no dot, append "._" to the name.
  1570. If there is a dot followed by 0, 1, or 2 charcaters, append "_".
  1571. Otherwise assume there is a 3-character extension and replace the
  1572. third character after the dot with "_".
  1573. Arguments:
  1574. Filename - supplies filename whose compressed form is desired.
  1575. CompressedName - supplies pointer to a 128-char buffer to
  1576. contain the compressed form.
  1577. Return Value:
  1578. None.
  1579. --*/
  1580. {
  1581. PCHAR p,q;
  1582. strcpy(CompressedName,Filename);
  1583. p = strrchr(CompressedName,'.');
  1584. q = strrchr(CompressedName,'\\');
  1585. if(q < p) {
  1586. //
  1587. // If there are 0, 1, or 2 characters after the dot, just append
  1588. // the underscore. p points to the dot so include that in the length.
  1589. //
  1590. if(strlen(p) < 4) {
  1591. strcat(CompressedName,"_");
  1592. } else {
  1593. //
  1594. // Assume there are 3 characters in the extension. So replace
  1595. // the final one with an underscore.
  1596. //
  1597. p[3] = '_';
  1598. }
  1599. } else {
  1600. //
  1601. // No dot, just add ._.
  1602. //
  1603. strcat(CompressedName,"._");
  1604. }
  1605. }
  1606. BOOLEAN
  1607. DnpOpenSourceFile(
  1608. IN PCHAR Filename,
  1609. OUT int *Handle,
  1610. OUT unsigned *Attribs,
  1611. OUT BOOLEAN *UsedCompressedName
  1612. )
  1613. /*++
  1614. Routine Description:
  1615. Open a file by name or by compressed name. If the previous call to
  1616. this function found the compressed name, then try to open the compressed
  1617. name first. Otherwise try to open the uncompressed name first.
  1618. Arguments:
  1619. Filename - supplies full path of file to open. This should be the
  1620. uncompressed form of the filename.
  1621. Handle - If successful, receives the id for the opened file.
  1622. Attribs - if successful receives dos file attributes.
  1623. UsedCompressedName - receives a flag indicating whether we found
  1624. the compressed form of the filename (TRUE) or not (FALSE).
  1625. Return Value:
  1626. TRUE if the file was opened successfully.
  1627. FALSE if not.
  1628. --*/
  1629. {
  1630. static BOOLEAN TryCompressedFirst = FALSE;
  1631. CHAR CompressedName[128];
  1632. PCHAR names[2];
  1633. int OrdCompressed,OrdUncompressed;
  1634. int i;
  1635. BOOLEAN rc;
  1636. //
  1637. // Generate compressed name.
  1638. //
  1639. DnpGenerateCompressedName(Filename,CompressedName);
  1640. //
  1641. // Figure out which name to try to use first. If the last successful
  1642. // call to this routine opened the file using the compressed name, then
  1643. // try to open the compressed name first. Otherwise try to open the
  1644. // uncompressed name first.
  1645. //
  1646. if(TryCompressedFirst) {
  1647. OrdCompressed = 0;
  1648. OrdUncompressed = 1;
  1649. } else {
  1650. OrdCompressed = 1;
  1651. OrdUncompressed = 0;
  1652. }
  1653. names[OrdUncompressed] = Filename;
  1654. names[OrdCompressed] = CompressedName;
  1655. for(i=0, rc=FALSE; (i<2) && !rc; i++) {
  1656. if(!_dos_open(names[i],O_RDONLY|SH_DENYWR,Handle)) {
  1657. _dos_getfileattr(names[i],Attribs);
  1658. TryCompressedFirst = (BOOLEAN)(i == OrdCompressed);
  1659. *UsedCompressedName = TryCompressedFirst;
  1660. rc = TRUE;
  1661. }
  1662. }
  1663. return(rc);
  1664. }
  1665. BOOLEAN
  1666. InDriverCacheInf(
  1667. IN PVOID InfHandle,
  1668. IN PCHAR FileName
  1669. )
  1670. {
  1671. PCHAR SectionName;
  1672. unsigned int i;
  1673. BOOLEAN ret = FALSE;
  1674. if( !InfHandle )
  1675. return FALSE;
  1676. i = 0;
  1677. do{
  1678. SectionName = DnGetSectionKeyIndex(InfHandle,"Version","CabFiles",i++);
  1679. //_LOG(("Looking in %s\n",SectionName));
  1680. if( SectionName ){
  1681. //
  1682. // Search sections for our entry
  1683. //
  1684. if( DnGetSectionEntryExists( InfHandle, SectionName, FileName)){
  1685. //_LOG(("Found %s in %s\n",FileName, SectionName));
  1686. ret = TRUE;
  1687. }
  1688. FREE( SectionName );
  1689. }
  1690. }while( !ret && SectionName );
  1691. //
  1692. // If we got here we did not find the file
  1693. //
  1694. return ret;
  1695. }
  1696. VOID
  1697. DnCopyFilesInSection(
  1698. IN unsigned Flags,
  1699. IN PCHAR SectionName,
  1700. IN PCHAR SourcePath,
  1701. IN PCHAR TargetPath
  1702. )
  1703. {
  1704. unsigned line;
  1705. PCHAR FileName;
  1706. PCHAR TargName;
  1707. CHAR p[128],q[128];
  1708. DnClearClientArea();
  1709. DnWriteStatusText(NULL);
  1710. line = 0;
  1711. while(FileName = DnGetSectionLineIndex(DngInfHandle,SectionName,line++,0)) {
  1712. TargName = DnGetSectionLineIndex(DngInfHandle,SectionName,line-1,1);
  1713. if(!TargName) {
  1714. TargName = FileName;
  1715. }
  1716. strcpy(p,SourcePath);
  1717. DnpConcatPaths(p,FileName);
  1718. strcpy(q,TargetPath);
  1719. DnpConcatPaths(q,TargName);
  1720. DnpCopyOneFile(Flags,p,q);
  1721. if (TargName != FileName) {
  1722. FREE (TargName);
  1723. }
  1724. FREE (FileName);
  1725. }
  1726. }
  1727. VOID
  1728. DnCopyOemBootFiles(
  1729. PCHAR TargetPath
  1730. )
  1731. {
  1732. unsigned Count;
  1733. CHAR p[128],q[128];
  1734. DnClearClientArea();
  1735. DnWriteStatusText(NULL);
  1736. for(Count=0; Count<OemBootFilesCount; Count++) {
  1737. if( UserSpecifiedOEMShare ) {
  1738. strcpy(p, UserSpecifiedOEMShare );
  1739. DnpConcatPaths(p,WINNT_OEM_TEXTMODE_DIR);
  1740. DnpConcatPaths(p,OemBootFiles[Count]);
  1741. } else {
  1742. strcpy(p, DngSourceRootPath );
  1743. DnpConcatPaths(p,WINNT_OEM_TEXTMODE_DIR);
  1744. DnpConcatPaths(p,OemBootFiles[Count]);
  1745. }
  1746. strcpy(q, TargetPath );
  1747. DnpConcatPaths(q, OemBootFiles[Count]);
  1748. DnpCopyOneFile(0,p,q);
  1749. }
  1750. }
  1751. #if NEC_98
  1752. VOID
  1753. DnCopyFilesInSectionForFDless(
  1754. IN PCHAR SectionName,
  1755. IN PCHAR SourcePath,
  1756. IN PCHAR TargetPath
  1757. )
  1758. /*++
  1759. Routine Description:
  1760. Copies the file in Section. for FD less setup.
  1761. SourcePath -> TargetPath
  1762. Arguments:
  1763. SectionName - [RootBootFiles] in dosnet.inf
  1764. SourcePath - Root directory.(temporary drive)
  1765. TargetPath - \$WIN_NT$.~BU(temporary drive)
  1766. Return Value:
  1767. None.
  1768. --*/
  1769. {
  1770. unsigned line;
  1771. PCHAR FileName;
  1772. PCHAR TargName;
  1773. PCHAR p,q;
  1774. DnClearClientArea();
  1775. DnWriteStatusText(NULL);
  1776. line = 0;
  1777. while(FileName = DnGetSectionLineIndex(DngInfHandle,SectionName,line++,0)) {
  1778. TargName = DnGetSectionLineIndex(DngInfHandle,SectionName,line-1,1);
  1779. if(!TargName) {
  1780. TargName = FileName;
  1781. }
  1782. p = MALLOC(strlen(SourcePath) + strlen(FileName) + 2,TRUE);
  1783. q = MALLOC(strlen(TargetPath) + strlen(TargName) + 2,TRUE);
  1784. sprintf(p,"%s\\%s",SourcePath,FileName);
  1785. sprintf(q,"%s\\%s",TargetPath,TargName);
  1786. DnpCopyOneFileForFDless(p,q,FALSE);
  1787. FREE(p);
  1788. FREE(q);
  1789. if (TargName != FileName) {
  1790. FREE (TargName);
  1791. }
  1792. FREE (FileName);
  1793. }
  1794. }
  1795. ULONG
  1796. DnpCopyOneFileForFDless(
  1797. IN PCHAR SourceName,
  1798. IN PCHAR DestName,
  1799. IN BOOLEAN Verify
  1800. )
  1801. /*++
  1802. Routine Description:
  1803. Copies a single file. for FD less setup.
  1804. Arguments:
  1805. SourceName - supplies fully qualified name of source file
  1806. DestName - supplies fully qualified name of destination file
  1807. Verify - if TRUE, the file will be verified after it has been copied.
  1808. Return Value:
  1809. None. May not return if an error occurs during the copy.
  1810. --*/
  1811. {
  1812. int SrcHandle,DstHandle;
  1813. PCHAR FilenamePart;
  1814. BOOLEAN Verified;
  1815. ULONG BytesWritten = 0;
  1816. unsigned attribs, verifyf = 0;
  1817. BOOLEAN UsedCompName;
  1818. FilenamePart = strrchr(SourceName,'\\') + 1;
  1819. DnSetCopyStatusText(DntCopying,FilenamePart);
  1820. if(DnpOpenSourceFile(SourceName, &SrcHandle, &attribs, &UsedCompName)) {
  1821. _dos_setfileattr(DestName,_A_NORMAL);
  1822. if (Verify)
  1823. verifyf = CPY_VERIFY;
  1824. if(!_dos_creat(DestName,_A_NORMAL,&DstHandle)) {
  1825. DnpDoCopyOneFile(verifyf, SrcHandle,DstHandle,FilenamePart,&Verified,&BytesWritten);
  1826. _dos_close(DstHandle);
  1827. }
  1828. _dos_close(SrcHandle);
  1829. }
  1830. return(BytesWritten);
  1831. }
  1832. #endif // NEC_98