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.

2220 lines
58 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. stf.c
  5. Abstract:
  6. Applications that use the ACME Setup toolkit leave .STF files with their
  7. installation, so ACME can reinstall or uninstall the application. During
  8. the upgrade, we move paths around, and we confuse ACME Setup to the point
  9. where it won't run.
  10. The routines in this file update all STF files found on the system. Each
  11. STF has an associated INF file, and the code here parses the STF and INF
  12. files into memory structures, and then enumerates the structures in
  13. various ways, updating all the paths.
  14. Entry points:
  15. ProcessStfFiles - Enumerates all STF files and processes those that have
  16. not already been handled in another way. The older
  17. STF files are overwritten.
  18. See the ACME Setup specification for more details on the format of STF
  19. files and their associated INFs.
  20. Author:
  21. Jim Schmidt (jimschm) 12-Sep-1997
  22. Revision History:
  23. jimschm 28-Sep-1998 Updated to change all altered dirs
  24. --*/
  25. #include "pch.h"
  26. #include "migmainp.h"
  27. #include "stftable.h"
  28. #include "fileops.h"
  29. #include "stfp.h"
  30. #define DBG_STF "STF"
  31. #define S_SECTIONNAME_SPRINTF TEXT("Win9xUpg_%u")
  32. #define COLUMN_OBJECT_ID 0
  33. #define COLUMN_COMMAND 4
  34. #define COLUMN_OBJECT_DATA 5
  35. #define COLUMN_DEST_DIR 10
  36. #define COLUMN_INSTALL_DESTDIR 14
  37. PVOID
  38. pBuildObjectIdTable (
  39. IN PSETUPTABLE TablePtr,
  40. OUT PUINT FirstLinePtr, OPTIONAL
  41. OUT PUINT LastLinePtr OPTIONAL
  42. );
  43. BOOL
  44. pIsObjIdValid (
  45. IN PVOID ObjIdTable,
  46. IN UINT ObjId,
  47. OUT PUINT Line OPTIONAL
  48. );
  49. BOOL
  50. ProcessStfFiles (
  51. VOID
  52. )
  53. /*++
  54. Routine Description:
  55. ProcessStfFiles enumerates the memdb category Stf and converts
  56. the paths in the STF to the new locations.
  57. Arguments:
  58. none
  59. Return Value:
  60. TRUE if success, FALSE if failure.
  61. --*/
  62. {
  63. MEMDB_ENUM e;
  64. DWORD ops;
  65. if (MemDbGetValueEx (&e, MEMDB_CATEGORY_STF, NULL, NULL)) {
  66. do {
  67. //
  68. // Is file handled? If so, skip it.
  69. //
  70. ops = GetOperationsOnPath (e.szName);
  71. if (ops & (OPERATION_MIGDLL_HANDLED|ALL_DELETE_OPERATIONS|OPERATION_FILE_DISABLED)) {
  72. continue;
  73. }
  74. //
  75. // Process the file
  76. //
  77. DEBUGMSG ((DBG_STF, "Processing %s", e.szName));
  78. if (!pProcessSetupTableFile (e.szName)) {
  79. //
  80. // Log the failure
  81. //
  82. LOG ((LOG_INFORMATION, (PCSTR)MSG_COULD_NOT_PROCESS_STF_LOG, e.szName));
  83. } else {
  84. TickProgressBar ();
  85. }
  86. } while (MemDbEnumNextValue (&e));
  87. }
  88. return TRUE;
  89. }
  90. BOOL
  91. pProcessSetupTable (
  92. IN OUT PSETUPTABLE TablePtr
  93. )
  94. /*++
  95. Routine Description:
  96. pProcessSetupTable scans the entire setup table file specified, looking
  97. for CopyFile, CopySection, RemoveFile or RemoveSection lines. If any are
  98. found, any paths that point to moved or deleted files are adjusted, and
  99. any STF group references are updated.
  100. Arguments:
  101. TablePtr - Specifies the setup table file to process
  102. Return Value:
  103. TRUE if success, FALSE if failure.
  104. --*/
  105. {
  106. UINT MaxObj;
  107. UINT Line;
  108. UINT Obj;
  109. PCTSTR EntryStr;
  110. PCTSTR DataStr;
  111. PCTSTR InstallDestDir;
  112. PCTSTR *ArgArray;
  113. PCTSTR p;
  114. UINT ArgCount;
  115. PTABLEENTRY Entry;
  116. TCHAR SystemDir[MAX_TCHAR_PATH];
  117. UINT SystemDirLen;
  118. PCTSTR UpdatedDir;
  119. PVOID ObjTable;
  120. TCHAR MovedPath[MAX_TCHAR_PATH];
  121. DWORD MovedPathLen;
  122. PCTSTR NewPath;
  123. PTSTR q;
  124. DWORD FileStatus;
  125. PTSTR *ListOfWacks;
  126. PTSTR *WackPos;
  127. BOOL Result = TRUE;
  128. MaxObj = TablePtr->MaxObj;
  129. if (TablePtr->SourceInfFile != INVALID_HANDLE_VALUE) {
  130. //
  131. // If an INF is specified, scan the STF/INF pair for references to moved files.
  132. // If found, correct the files.
  133. //
  134. for (Line = 0 ; Line < TablePtr->LineCount ; Line++) {
  135. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_ID, NULL, &EntryStr)) {
  136. continue;
  137. }
  138. Obj = _ttoi (EntryStr);
  139. if (Obj < 1 || Obj > MaxObj) {
  140. continue;
  141. }
  142. //
  143. // CopySection or RemoveSection: Data column has INF section name
  144. //
  145. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_COMMAND, NULL, &EntryStr)) {
  146. continue;
  147. }
  148. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL, &DataStr)) {
  149. continue;
  150. }
  151. InstallDestDir = GetDestDir (TablePtr, Line);
  152. if (!InstallDestDir) {
  153. continue;
  154. }
  155. ArgArray = ParseCommaList (TablePtr, DataStr);
  156. if (!ArgArray) {
  157. continue;
  158. }
  159. for (ArgCount = 0 ; ArgArray[ArgCount] ; ArgCount++) {
  160. // empty
  161. }
  162. __try {
  163. if (StringIMatch (EntryStr, TEXT("CopySection")) ||
  164. StringIMatch (EntryStr, TEXT("RemoveSection"))
  165. ) {
  166. if (ArgCount != 1) {
  167. continue;
  168. }
  169. if (!pProcessSectionCommand (TablePtr, Line, ArgArray[0], InstallDestDir)) {
  170. DEBUGMSG ((DBG_STF, "%s [%s] could not be processed", EntryStr, ArgArray[0]));
  171. Result = FALSE;
  172. __leave;
  173. }
  174. }
  175. else if (StringIMatch (EntryStr, TEXT("CopyFile")) ||
  176. StringIMatch (EntryStr, TEXT("RemoveFile")) ||
  177. StringIMatch (EntryStr, TEXT("InstallSysFile"))
  178. ) {
  179. if (ArgCount != 2) {
  180. continue;
  181. }
  182. if (!pProcessLineCommand (TablePtr, Line, ArgArray[0], ArgArray[1], InstallDestDir)) {
  183. DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
  184. Result = FALSE;
  185. __leave;
  186. }
  187. }
  188. else if (StringIMatch (EntryStr, TEXT("CompanionFile"))) {
  189. if (ArgCount != 2) {
  190. continue;
  191. }
  192. // First arg has a colon -- skip past it
  193. p = _tcschr (ArgArray[0], TEXT(':'));
  194. if (!p) {
  195. continue;
  196. }
  197. p = SkipSpace (_tcsinc (p));
  198. if (!pProcessLineCommand (TablePtr, Line, p, ArgArray[1], InstallDestDir)) {
  199. DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
  200. Result = FALSE;
  201. __leave;
  202. }
  203. }
  204. else if (StringIMatch (EntryStr, TEXT("InstallShared"))) {
  205. if (ArgCount != 5) {
  206. continue;
  207. }
  208. if (!pProcessLineCommand (TablePtr, Line, ArgArray[0], ArgArray[1], InstallDestDir)) {
  209. DEBUGMSG ((DBG_STF, "%s [%s] %s could not be processed", EntryStr, ArgArray[0], ArgArray[1]));
  210. Result = FALSE;
  211. __leave;
  212. }
  213. }
  214. }
  215. __finally {
  216. FreeDestDir (TablePtr, InstallDestDir);
  217. FreeCommaList (TablePtr, ArgArray);
  218. }
  219. }
  220. }
  221. //
  222. // Perform STF-only processing
  223. //
  224. SystemDirLen = wsprintf (SystemDir, TEXT("%s\\system\\"), g_WinDir);
  225. ObjTable = pBuildObjectIdTable (TablePtr, NULL, NULL);
  226. __try {
  227. for (Line = 0 ; Line < TablePtr->LineCount ; Line++) {
  228. //
  229. // Get InstallDestDir and Entry from the line that needs to be modified.
  230. //
  231. if (!pGetNonEmptyTableEntry (
  232. TablePtr,
  233. Line,
  234. COLUMN_INSTALL_DESTDIR,
  235. &Entry,
  236. &InstallDestDir
  237. )) {
  238. continue;
  239. }
  240. //
  241. // If InstallDestDir has a %windir%\system in it, we must adjust the path
  242. // to point to system32.
  243. //
  244. if (StringIMatchCharCount (InstallDestDir, SystemDir, SystemDirLen)) {
  245. UpdatedDir = JoinPaths (
  246. g_System32Dir,
  247. CharCountToPointer (InstallDestDir, SystemDirLen - 1)
  248. );
  249. if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedDir)) {
  250. LOG ((LOG_ERROR, "Could not replace a %%M path"));
  251. Result = FALSE;
  252. }
  253. FreePathString (UpdatedDir);
  254. if (!Result) {
  255. __leave;
  256. }
  257. }
  258. //
  259. // If InstallDestDir points to a moved dir, we must fix it
  260. //
  261. else if (*InstallDestDir && _tcsnextc (_tcsinc (InstallDestDir)) == TEXT(':')) {
  262. //
  263. // Build list of wacks in the path
  264. //
  265. ListOfWacks = (PTSTR *) MemAlloc (g_hHeap, 0, sizeof (PTSTR) * MAX_TCHAR_PATH);
  266. MYASSERT (ListOfWacks);
  267. StringCopy (MovedPath, InstallDestDir);
  268. q = _tcschr (MovedPath, TEXT('\\'));
  269. WackPos = ListOfWacks;
  270. if (q) {
  271. while (*q) {
  272. if (_tcsnextc (q) == TEXT('\\')) {
  273. *WackPos = q;
  274. WackPos++;
  275. }
  276. q = _tcsinc (q);
  277. }
  278. //
  279. // We assume the STF always has an extra wack at the end
  280. // of the path.
  281. //
  282. //
  283. // Test each path from longest to shortest, skipping the root
  284. //
  285. FileStatus = FILESTATUS_UNCHANGED;
  286. while (WackPos > ListOfWacks) {
  287. WackPos--;
  288. q = *WackPos;
  289. *q = 0;
  290. FileStatus = GetFileStatusOnNt (MovedPath);
  291. if (FileStatus == FILESTATUS_MOVED) {
  292. break;
  293. }
  294. DEBUGMSG_IF ((
  295. FileStatus != FILESTATUS_UNCHANGED,
  296. DBG_WARNING,
  297. "STF may point to changed dir: %s",
  298. MovedPath
  299. ));
  300. }
  301. if (FileStatus == FILESTATUS_MOVED) {
  302. //
  303. // Adjust the STF path
  304. //
  305. NewPath = GetPathStringOnNt (MovedPath);
  306. if (NewPath) {
  307. MovedPathLen = (PBYTE) q - (PBYTE) MovedPath;
  308. UpdatedDir = JoinPaths (
  309. NewPath,
  310. ByteCountToPointer (InstallDestDir, MovedPathLen)
  311. );
  312. DEBUGMSG ((
  313. DBG_STF,
  314. "Line %u has a new install destination: %s",
  315. Line,
  316. UpdatedDir
  317. ));
  318. if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedDir)) {
  319. LOG ((LOG_ERROR, "Could not replace a moved path"));
  320. Result = FALSE;
  321. }
  322. FreePathString (UpdatedDir);
  323. FreePathString (NewPath);
  324. if (!Result) {
  325. __leave;
  326. }
  327. }
  328. }
  329. }
  330. MemFree (g_hHeap, 0, ListOfWacks);
  331. }
  332. }
  333. }
  334. __finally {
  335. if (ObjTable) {
  336. pSetupStringTableDestroy (ObjTable);
  337. }
  338. }
  339. //
  340. // Update all the lines that reference other OBJs. This includes GROUP,
  341. // DEPEND and COMPANIONFILE lines.
  342. //
  343. return Result && pUpdateObjReferences (TablePtr);
  344. }
  345. PVOID
  346. pBuildObjectIdTable (
  347. IN PSETUPTABLE TablePtr,
  348. OUT PUINT FirstLinePtr, OPTIONAL
  349. OUT PUINT LastLinePtr OPTIONAL
  350. )
  351. {
  352. PVOID ObjIds;
  353. UINT FirstLine, LastLine;
  354. UINT Line;
  355. UINT Obj;
  356. UINT MaxObj;
  357. PCTSTR EntryStr;
  358. TCHAR NumBuf[32];
  359. MaxObj = TablePtr->MaxObj;
  360. //
  361. // Alloc string table
  362. //
  363. ObjIds = pSetupStringTableInitializeEx (sizeof (DWORD), 0);
  364. if (!ObjIds) {
  365. LOG ((LOG_ERROR, "STF: Can't init string table"));
  366. return NULL;
  367. }
  368. //
  369. // Fill string table with list of ObjIDs
  370. //
  371. FirstLine = 0;
  372. LastLine = TablePtr->LineCount;
  373. for (Line = 0 ; Line < LastLine ; Line++) {
  374. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_ID, NULL, &EntryStr)) {
  375. continue;
  376. }
  377. Obj = _ttoi (EntryStr);
  378. if (Obj < 1 || Obj > MaxObj) {
  379. continue;
  380. }
  381. if (!FirstLine) {
  382. FirstLine = Line;
  383. }
  384. wsprintf (NumBuf, TEXT("%u"), Obj);
  385. if (-1 == pSetupStringTableAddStringEx (
  386. ObjIds,
  387. NumBuf,
  388. STRTAB_CASE_SENSITIVE,
  389. (PBYTE) &Line,
  390. sizeof (DWORD)
  391. )) {
  392. LOG ((LOG_ERROR, "STF: Can't add to string table"));
  393. break;
  394. }
  395. }
  396. if (FirstLinePtr) {
  397. *FirstLinePtr = FirstLine;
  398. }
  399. if (LastLinePtr) {
  400. *LastLinePtr = LastLine;
  401. }
  402. return ObjIds;
  403. }
  404. BOOL
  405. pIsObjIdValid (
  406. IN PVOID ObjIdTable,
  407. IN UINT ObjId,
  408. OUT PUINT Line OPTIONAL
  409. )
  410. {
  411. TCHAR NumBuf[32];
  412. LONG rc;
  413. DWORD LineData;
  414. wsprintf (NumBuf, TEXT("%u"), ObjId);
  415. rc = pSetupStringTableLookUpStringEx (
  416. ObjIdTable,
  417. NumBuf,
  418. STRTAB_CASE_SENSITIVE|STRTAB_BUFFER_WRITEABLE,
  419. (PBYTE) &LineData,
  420. sizeof (DWORD)
  421. );
  422. if (Line && rc != -1) {
  423. *Line = LineData;
  424. }
  425. return rc != -1;
  426. }
  427. PCTSTR
  428. pValidateGroup (
  429. IN PSETUPTABLE TablePtr,
  430. IN PCTSTR EntryStr,
  431. IN PVOID ObjIds
  432. )
  433. /*++
  434. Routine Description:
  435. pValidateGroup parses all object IDs in the EntryStr, compares them against
  436. the string table ObjIds, and adds only those IDs that are in both EntryStr
  437. and ObjIds. The caller receives a text pool string, which may be
  438. empty.
  439. The buffer must be freed by the caller with FreeText.
  440. Arguments:
  441. TablePtr - Specifies the setup table being processed
  442. EntryStr - Specifies the entry string that contains zero or more numeric
  443. object ID references, sparated by spaces.
  444. ObjIds - Specifies the string table of valid object IDs.
  445. Return Value:
  446. A pointer to the validated object ID string, or NULL if memory allocation
  447. failed.
  448. --*/
  449. {
  450. PTSTR Buffer;
  451. PTSTR p;
  452. PCTSTR q;
  453. CHARTYPE ch;
  454. UINT Obj;
  455. TCHAR NumBuf[32];
  456. //
  457. // Validate EntryStr
  458. //
  459. Buffer = AllocText (CharCount (EntryStr) + 1);
  460. if (!Buffer) {
  461. return NULL;
  462. }
  463. p = Buffer;
  464. *p = 0;
  465. q = EntryStr;
  466. while (*q) {
  467. ch = (CHARTYPE)_tcsnextc (q);
  468. if (ch >= TEXT('0') && ch <= TEXT('9')) {
  469. //
  470. // Extract object ID reference
  471. //
  472. Obj = 0;
  473. for (;;) {
  474. ch = (CHARTYPE)_tcsnextc (q);
  475. if (ch >= TEXT('0') && ch <= TEXT('9')) {
  476. Obj = Obj * 10 + (ch - TEXT('0'));
  477. } else {
  478. break;
  479. }
  480. q = _tcsinc (q);
  481. }
  482. //
  483. // If match found, add obj ID to data
  484. //
  485. if (pIsObjIdValid (ObjIds, Obj, NULL)) {
  486. wsprintf (NumBuf, TEXT("%u"), Obj);
  487. p = _tcsappend (p, NumBuf);
  488. }
  489. } else {
  490. _copytchar (p, q);
  491. p = _tcsinc (p);
  492. *p = 0;
  493. q = _tcsinc (q);
  494. }
  495. }
  496. return Buffer;
  497. }
  498. BOOL
  499. pUpdateObjReferences (
  500. IN PSETUPTABLE TablePtr
  501. )
  502. /*++
  503. Routine Description:
  504. pUpdateObjReferences scans the specified table for GROUP, DEPEND and COMPANIONFILE
  505. lines, and for each line found, the line is updated.
  506. In the case of a GROUP line, the data argument is updated if it points to one or
  507. more invalid object IDs. If the cleanup operation causes a group to have zero
  508. items, the group line itself is deleted, and the update is restarted.
  509. In the case of a DEPEND line, if the first object ID no longer exists, then
  510. the line is deleted, and the update is restarted. If an obj in the group
  511. following the ? no longer exists, then the obj reference is deleted. If the
  512. delete causes no objects to be listed, then the line is deleted.
  513. In the case of a COMPANIONFILE line, the object ID is extracted from the data
  514. argument, and the line is deleted if its original line is gone.
  515. NOTE: This routine has a lot of exit conditions that cause leaks, but all of them
  516. can only be hit by memory allocation failures
  517. Arguments:
  518. TablePtr - Specifies the setup table file to process
  519. Return Value:
  520. TRUE if processing was successful, or FALSE if an error occured. FALSE will cause
  521. STF processing to fail for the current STF, and will likely generate an error log
  522. entry.
  523. --*/
  524. {
  525. UINT Line;
  526. PVOID ObjIds;
  527. PCTSTR EntryStr;
  528. UINT Obj;
  529. BOOL b = FALSE;
  530. UINT FirstLine, LastLine;
  531. PTSTR Buffer;
  532. PTSTR DependBuf;
  533. PTSTR p;
  534. BOOL StartOver;
  535. PTABLEENTRY Entry;
  536. BOOL GroupMode;
  537. BOOL CompanionFileMode;
  538. BOOL DependMode;
  539. do {
  540. StartOver = FALSE;
  541. ObjIds = pBuildObjectIdTable (TablePtr, &FirstLine, &LastLine);
  542. if (!ObjIds) {
  543. return FALSE;
  544. }
  545. Line = TablePtr->LineCount;
  546. if (!FirstLine) {
  547. //
  548. // Small table -- no object IDs at all! Return TRUE to caller.
  549. //
  550. b = TRUE;
  551. Line = 0;
  552. }
  553. //
  554. // Look for lines that have object ID references
  555. //
  556. if (Line == TablePtr->LineCount) {
  557. for (Line = FirstLine ; !StartOver && Line < LastLine ; Line++) {
  558. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_COMMAND, NULL, &EntryStr)) {
  559. continue;
  560. }
  561. GroupMode = StringIMatch (EntryStr, TEXT("Group"));
  562. CompanionFileMode = StringIMatch (EntryStr, TEXT("CompanionFile"));
  563. DependMode = StringIMatch (EntryStr, TEXT("Depend"));
  564. if (!GroupMode && !CompanionFileMode && !DependMode) {
  565. continue;
  566. }
  567. if (!pGetNonEmptyTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL, &EntryStr)) {
  568. continue;
  569. }
  570. if (GroupMode) {
  571. Buffer = (PTSTR) pValidateGroup (TablePtr, EntryStr, ObjIds);
  572. if (!Buffer) {
  573. break;
  574. }
  575. //
  576. // If Buffer is empty, delete the group line, then start over
  577. //
  578. if (*Buffer == 0) {
  579. pDeleteStfLine (TablePtr, Line);
  580. StartOver = TRUE;
  581. DEBUGMSG ((
  582. DBG_STF,
  583. "Group line %u references only deleted lines, so it was deleted as well.",
  584. Line
  585. ));
  586. }
  587. //
  588. // If Buffer is not empty, replace the data on the current line
  589. //
  590. else if (!StringMatch (EntryStr, Buffer)) {
  591. DEBUGMSG ((
  592. DBG_STF,
  593. "Group has reference to one or more deleted objects. Original: %s New: %s",
  594. EntryStr,
  595. Buffer
  596. ));
  597. Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL);
  598. MYASSERT (Entry);
  599. if (Entry) {
  600. if (!ReplaceTableEntryStr (TablePtr, Entry, Buffer)) {
  601. break;
  602. }
  603. }
  604. }
  605. FreeText (Buffer);
  606. }
  607. if (!StartOver && (DependMode || CompanionFileMode)) {
  608. //
  609. // Extract the obj ID from the data arg
  610. //
  611. Obj = _ttoi (EntryStr);
  612. if (Obj || EntryStr[0] == TEXT('0')) {
  613. if (!pIsObjIdValid (ObjIds, Obj, NULL)) {
  614. //
  615. // CompanionFile/Depend is for a line that was deleted. Delete
  616. // the line and start over.
  617. //
  618. pDeleteStfLine (TablePtr, Line);
  619. StartOver = TRUE;
  620. DEBUGMSG_IF ((
  621. CompanionFileMode,
  622. DBG_STF,
  623. "CompanionFile line %u references a deleted line %u, so it was deleted as well.",
  624. Line,
  625. Obj
  626. ));
  627. DEBUGMSG_IF ((
  628. DependMode,
  629. DBG_STF,
  630. "Depend line %u references a deleted line %u, so it was deleted as well.",
  631. Line,
  632. Obj
  633. ));
  634. }
  635. }
  636. }
  637. if (!StartOver && DependMode) {
  638. //
  639. // Go beyond question mark, then validate group
  640. //
  641. p = _tcschr (EntryStr, TEXT('?'));
  642. if (p) {
  643. p = _tcsinc (p);
  644. while (*p == TEXT(' ')) {
  645. p++;
  646. }
  647. Buffer = (PTSTR) pValidateGroup (TablePtr, p, ObjIds);
  648. if (!Buffer) {
  649. break;
  650. }
  651. if (*Buffer == 0) {
  652. pDeleteStfLine (TablePtr, Line);
  653. StartOver = TRUE;
  654. DEBUGMSG ((
  655. DBG_STF,
  656. "Depend line %u references only deleted lines, so it was deleted as well.",
  657. Line
  658. ));
  659. }
  660. //
  661. // If Buffer is not empty, replace the data on the current line
  662. //
  663. else if (!StringMatch (p, Buffer)) {
  664. DependBuf = AllocText (ByteCount (Buffer) + 32);
  665. if (!DependBuf) {
  666. break;
  667. }
  668. StringCopyAB (DependBuf, EntryStr, p);
  669. StringCat (DependBuf, Buffer);
  670. DEBUGMSG ((
  671. DBG_STF,
  672. "Depend line has reference to one or more deleted objects. Original: %s New: %s",
  673. EntryStr,
  674. DependBuf
  675. ));
  676. Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, NULL);
  677. MYASSERT (Entry);
  678. if (Entry) {
  679. if (!ReplaceTableEntryStr (TablePtr, Entry, DependBuf)) {
  680. break;
  681. }
  682. }
  683. FreeText (DependBuf);
  684. }
  685. FreeText (Buffer);
  686. }
  687. }
  688. }
  689. //
  690. // If we managed to get through the loop, we are done! Return TRUE to caller.
  691. //
  692. if (Line == LastLine) {
  693. b = TRUE;
  694. }
  695. }
  696. pSetupStringTableDestroy (ObjIds);
  697. } while (StartOver);
  698. return b;
  699. }
  700. BOOL
  701. pGetNonEmptyTableEntry (
  702. IN PSETUPTABLE TablePtr,
  703. IN UINT Line,
  704. IN UINT Col,
  705. OUT PTABLEENTRY *EntryPtr, OPTIONAL
  706. OUT PCTSTR *EntryStr
  707. )
  708. /*++
  709. Routine Description:
  710. pGetNonEmptyTableEntry is a wrapper routine that gets an entry in the
  711. STF table and returns TRUE only if the string actually exists and is
  712. not empty. If a non-empty string is found, the pointer is returned to
  713. the caller.
  714. Arguments:
  715. TablePtr - Specifies the setup table file to process
  716. Line - Specifies the line to get the entry for
  717. Col - Specifies the column on the line to get the entry for
  718. EntryPtr - Receives a pointer to the entry struct
  719. EntryStr - Receives a pointer to the entry string
  720. Return Value:
  721. TRUE if entry exists and is not empty, FALSE if the entry does not exist
  722. or is empty.
  723. --*/
  724. {
  725. PCTSTR String;
  726. PTABLEENTRY Entry;
  727. Entry = GetTableEntry (TablePtr, Line, Col, &String);
  728. if (!Entry) {
  729. return FALSE;
  730. }
  731. if (!String || !String[0]) {
  732. return FALSE;
  733. }
  734. if (EntryPtr) {
  735. *EntryPtr = Entry;
  736. }
  737. if (EntryStr) {
  738. *EntryStr = String;
  739. }
  740. return TRUE;
  741. }
  742. PCTSTR
  743. pQuoteThis (
  744. IN PCTSTR String
  745. )
  746. {
  747. static TCHAR Buffer[128];
  748. MYASSERT (ByteCount (String) < (sizeof (Buffer) - 2));
  749. Buffer[0] = TEXT('\"');
  750. StringCopy (&Buffer[1], String);
  751. StringCat (Buffer, TEXT("\""));
  752. return Buffer;
  753. }
  754. BOOL
  755. pProcessSectionCommand (
  756. IN OUT PSETUPTABLE TablePtr,
  757. IN UINT StfLine,
  758. IN PCTSTR InfSection,
  759. IN PCTSTR InstallDestDir
  760. )
  761. /*++
  762. Routine Description:
  763. pProcessSectionCommand scans an INF section, determining which files
  764. are deleted, moved or unchanged. If a file is moved or unchanged,
  765. it is added to memdb. After the INF section is completely scanned,
  766. the memdb structure is processed, causing additional INF sections
  767. to be generated if any changes were made to the file paths.
  768. Arguments:
  769. TablePtr - Specifies the setup table file to process
  770. StfLine - Specifies the STF line that has a CopySection or RemoveSection
  771. command.
  772. InfSection - Specifies the INF section that lists files to be processed
  773. InstallDestDir - Specifies destination directory specified by STF line
  774. Return Value:
  775. TRUE if success, FALSE if failure.
  776. --*/
  777. {
  778. BOOL DeletedOrMoved = FALSE;
  779. PSTFINFLINE InfLine;
  780. PCTSTR * InfFields;
  781. UINT Fields;
  782. TCHAR FileName[MAX_TCHAR_PATH];
  783. TCHAR FullPath[MAX_TCHAR_PATH * 2];
  784. MEMDB_ENUM e;
  785. CONVERTPATH_RC rc;
  786. BOOL FirstSectionDone;
  787. BOOL CreatedFlag;
  788. PSTFINFSECTION NewInfSection;
  789. PSTFINFLINE SrcInfLine;
  790. PTSTR UpdatedData;
  791. TCHAR DirPart[MAX_TCHAR_PATH];
  792. PCTSTR FilePart;
  793. PTSTR p, q;
  794. PTABLEENTRY DataEntry;
  795. PCTSTR *Array;
  796. //
  797. // Step one: scan all files in the corresponding INF section,
  798. // moving them to memdb.
  799. //
  800. InfLine = StfGetFirstLineInSectionStr (TablePtr, InfSection);
  801. if (!InfLine) {
  802. //
  803. // Workaround: sometimes the people who write the STFs embed all kinds of
  804. // quotes in a section name.
  805. //
  806. Array = ParseCommaList (TablePtr, InfSection);
  807. if (Array) {
  808. if (Array[0]) {
  809. InfLine = StfGetFirstLineInSectionStr (TablePtr, Array[0]);
  810. }
  811. FreeCommaList (TablePtr, Array);
  812. }
  813. }
  814. if (!InfLine) {
  815. MYASSERT(InfSection);
  816. DEBUGMSG ((DBG_STF, "STF file references section %s that does not exist", InfSection));
  817. return TRUE;
  818. }
  819. __try {
  820. do {
  821. //
  822. // Parse the INF line into fields
  823. //
  824. InfFields = ParseCommaList (TablePtr, InfLine->Data);
  825. if (!InfFields) {
  826. MYASSERT(InfLine->Data);
  827. DEBUGMSG ((DBG_WARNING, "INF file has non-parsable data", InfLine->Data));
  828. } else {
  829. for (Fields = 0 ; InfFields[Fields] ; Fields++) {
  830. }
  831. if (Fields < 19) {
  832. MYASSERT(InfLine->Data);
  833. DEBUGMSG ((DBG_WARNING, "INF file line %s has less than 19 fields", InfLine->Data));
  834. } else {
  835. //
  836. // Get the file name from this INF line (field #2)
  837. //
  838. pGetFileNameFromInfField (FileName, InfFields[1]);
  839. StringCopy (FullPath, InstallDestDir);
  840. StringCat (AppendPathWack (FullPath), FileName);
  841. rc = ConvertWin9xPath (FullPath);
  842. if (rc != CONVERTPATH_NOT_REMAPPED) {
  843. DeletedOrMoved = TRUE;
  844. }
  845. if (rc != CONVERTPATH_DELETED) {
  846. //
  847. // Add this file to memdb
  848. //
  849. if (!MemDbSetValueEx (
  850. MEMDB_CATEGORY_STF_TEMP,
  851. FullPath,
  852. NULL,
  853. NULL,
  854. (DWORD) InfLine,
  855. NULL
  856. )) {
  857. LOG ((LOG_ERROR, "STF: MemDbSetValueEx failed"));
  858. return FALSE;
  859. }
  860. }
  861. }
  862. FreeCommaList (TablePtr, InfFields);
  863. }
  864. InfLine = StfGetNextLineInSection (InfLine);
  865. } while (InfLine);
  866. if (!DeletedOrMoved) {
  867. //
  868. // No changes necessary
  869. //
  870. return TRUE;
  871. }
  872. //
  873. // Now write out each unique directory to the INF. Update
  874. // the STF line to point to the first new INF section.
  875. //
  876. FirstSectionDone = FALSE;
  877. if (MemDbGetValueEx (&e, MEMDB_CATEGORY_STF_TEMP, NULL, NULL)) {
  878. do {
  879. //
  880. // Name gives full path of new file location.
  881. // Value points to INF line that is to be copied.
  882. //
  883. NewInfSection = pGetNewInfSection (TablePtr, e.szName, &CreatedFlag);
  884. if (!NewInfSection) {
  885. LOG ((LOG_ERROR, "Process Section Command failed because Get New Inf Section failed"));
  886. return FALSE;
  887. }
  888. SrcInfLine = (PSTFINFLINE) e.dwValue;
  889. _tcssafecpy (DirPart, e.szName, MAX_TCHAR_PATH);
  890. FilePart = GetFileNameFromPath (DirPart);
  891. MYASSERT (FilePart && FilePart > DirPart);
  892. *_tcsdec2 (DirPart, FilePart) = 0;
  893. //
  894. // File name may have changed. If so, specify the file name in the
  895. // angle brackets.
  896. //
  897. UpdatedData = DuplicatePathString (
  898. SrcInfLine->Data,
  899. SizeOfString (FilePart) + 2 * sizeof (TCHAR)
  900. );
  901. p = _tcschr (SrcInfLine->Data, TEXT(','));
  902. MYASSERT(p);
  903. p = _tcsinc (p);
  904. q = _tcschr (p, TEXT(','));
  905. MYASSERT(q);
  906. p = _tcschr (p, TEXT('<'));
  907. if (!p || p > q) {
  908. p = q;
  909. }
  910. StringCopyAB (UpdatedData, SrcInfLine->Data, q);
  911. wsprintf (_tcschr (UpdatedData, 0), TEXT("<%s>"), FilePart);
  912. StringCat (UpdatedData, q);
  913. DEBUGMSG ((DBG_STF, "INF changed from %s to %s", SrcInfLine->Data, UpdatedData));
  914. StfAddInfLineToTable (TablePtr, NewInfSection, SrcInfLine->Key, UpdatedData, SrcInfLine->LineFlags);
  915. //
  916. // If first section, update STF line to use new section
  917. //
  918. if (!FirstSectionDone) {
  919. DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
  920. if (!ReplaceTableEntryStr (TablePtr, DataEntry, pQuoteThis (NewInfSection->Name))) {
  921. LOG ((LOG_ERROR, "Could not update table entry"));
  922. return FALSE;
  923. }
  924. FirstSectionDone = TRUE;
  925. }
  926. //
  927. // If not first section and CreateFlag is TRUE, create a new STF line
  928. // and point it to new INF section.
  929. //
  930. else if (CreatedFlag) {
  931. if (!pCreateNewStfLine (TablePtr, StfLine, pQuoteThis (NewInfSection->Name), DirPart)) {
  932. LOG ((LOG_ERROR, "Could not create a new line"));
  933. return FALSE;
  934. }
  935. }
  936. } while (MemDbEnumNextValue (&e));
  937. } else {
  938. //
  939. // All files were deleted, and this STF line is no longer needed.
  940. //
  941. DEBUGMSG ((DBG_STF, "STF Line %u is no longer needed", StfLine));
  942. if (!pReplaceDirReferences (TablePtr, StfLine, InstallDestDir)) {
  943. return FALSE;
  944. }
  945. if (!pDeleteStfLine (TablePtr, StfLine)) {
  946. return FALSE;
  947. }
  948. }
  949. }
  950. __finally {
  951. MemDbDeleteTree (MEMDB_CATEGORY_STF_TEMP);
  952. MemDbDeleteTree (MEMDB_CATEGORY_STF_SECTIONS);
  953. }
  954. return TRUE;
  955. }
  956. VOID
  957. pGenerateUniqueKeyName (
  958. IN PSETUPTABLE TablePtr,
  959. IN PSTFINFSECTION Section,
  960. IN PCTSTR Root,
  961. OUT PTSTR UniqueKey
  962. )
  963. {
  964. UINT Sequencer = 0;
  965. PTSTR p;
  966. UniqueKey[0] = 0;
  967. p = _tcsappend (UniqueKey, Root);
  968. for (;;) {
  969. Sequencer++;
  970. wsprintf (p, TEXT("%03u"), Sequencer);
  971. if (!StfFindLineInInfSection (TablePtr, Section, UniqueKey)) {
  972. break;
  973. }
  974. }
  975. }
  976. BOOL
  977. pProcessLineCommand (
  978. IN OUT PSETUPTABLE TablePtr,
  979. IN UINT StfLine,
  980. IN PCTSTR InfSection,
  981. IN PCTSTR InfKey,
  982. IN PCTSTR InstallDestDir
  983. )
  984. /*++
  985. Routine Description:
  986. pProcessLineCommand determinins if the file assoicated with the command
  987. was deleted, moved or unchanged. If the file was deleted, the STF line
  988. is deleted. If the file was moved, the STF line is adjusted. If the
  989. file has no change, the routine does not modify the STF table.
  990. Arguments:
  991. TablePtr - Specifies the setup table file to process
  992. StfLine - Specifies the STF line that has a CopySection or RemoveSection
  993. command.
  994. InfSection - Specifies the INF section that lists the file to be processed
  995. InfKey - Specifies the INF key in InfSection identifing the file
  996. InstallDestDir - Specifies destination directory specified by STF line
  997. Return Value:
  998. TRUE if success, FALSE if failure.
  999. --*/
  1000. {
  1001. PSTFINFSECTION Section;
  1002. PSTFINFLINE InfLine;
  1003. PCTSTR *InfFields;
  1004. UINT Fields;
  1005. TCHAR FileName[MAX_TCHAR_PATH];
  1006. TCHAR FullPath[MAX_TCHAR_PATH * 2];
  1007. CONVERTPATH_RC rc;
  1008. TCHAR OrgDirPart[MAX_TCHAR_PATH];
  1009. PCTSTR OrgFilePart;
  1010. TCHAR DirPart[MAX_TCHAR_PATH];
  1011. PCTSTR FilePart;
  1012. PTABLEENTRY DataEntry;
  1013. PTABLEENTRY FileEntry;
  1014. PCTSTR *Array;
  1015. TCHAR NewKeyName[MAX_TCHAR_PATH];
  1016. PTSTR NewLine;
  1017. PTSTR p;
  1018. UINT Size;
  1019. PCTSTR OldField;
  1020. Section = StfFindInfSectionInTable (TablePtr, InfSection);
  1021. if (!Section) {
  1022. //
  1023. // Workaround: sometimes the people who write the STFs embed all kinds of
  1024. // quotes in a section name.
  1025. //
  1026. Array = ParseCommaList (TablePtr, InfSection);
  1027. if (Array) {
  1028. if (Array[0]) {
  1029. Section = StfFindInfSectionInTable (TablePtr, Array[0]);
  1030. }
  1031. FreeCommaList (TablePtr, Array);
  1032. }
  1033. }
  1034. if (!Section) {
  1035. MYASSERT(InfSection);
  1036. DEBUGMSG ((
  1037. DBG_STF,
  1038. "STF has reference to non-existent INF section ([%s])",
  1039. InfSection
  1040. ));
  1041. return TRUE;
  1042. }
  1043. InfLine = StfFindLineInInfSection (TablePtr, Section, InfKey);
  1044. if (!InfLine) {
  1045. MYASSERT(InfSection && InfKey);
  1046. DEBUGMSG ((
  1047. DBG_STF,
  1048. "STF has reference to non-existent INF key ([%s], %s)",
  1049. InfSection,
  1050. InfKey
  1051. ));
  1052. return TRUE;
  1053. }
  1054. //
  1055. // Build full path
  1056. //
  1057. InfFields = ParseCommaList (TablePtr, InfLine->Data);
  1058. __try {
  1059. if (!InfFields) {
  1060. MYASSERT(InfLine->Data);
  1061. DEBUGMSG ((DBG_WARNING, "INF file has non-parsable data", InfLine->Data));
  1062. return TRUE;
  1063. }
  1064. for (Fields = 0 ; InfFields[Fields] ; Fields++) {
  1065. /* empty */
  1066. }
  1067. if (Fields < 19) {
  1068. MYASSERT(InfLine->Data);
  1069. DEBUGMSG ((DBG_WARNING, "INF file line %s has less than 19 fields", InfLine->Data));
  1070. return TRUE;
  1071. }
  1072. //
  1073. // Get the file name from this INF line (field #2)
  1074. //
  1075. pGetFileNameFromInfField (FileName, InfFields[1]);
  1076. StringCopy (FullPath, InstallDestDir);
  1077. StringCopy (AppendPathWack (FullPath), FileName);
  1078. }
  1079. __finally {
  1080. FreeCommaList (TablePtr, InfFields);
  1081. }
  1082. //
  1083. // Determine mapping
  1084. //
  1085. _tcssafecpy (OrgDirPart, FullPath, MAX_TCHAR_PATH);
  1086. OrgFilePart = GetFileNameFromPath (OrgDirPart);
  1087. if (OrgFilePart <= OrgDirPart) {
  1088. // File probably wasn't installed
  1089. return TRUE;
  1090. }
  1091. *_tcsdec2 (OrgDirPart, OrgFilePart) = 0;
  1092. rc = ConvertWin9xPath (FullPath);
  1093. _tcssafecpy (DirPart, FullPath, MAX_TCHAR_PATH);
  1094. FilePart = GetFileNameFromPath (DirPart);
  1095. MYASSERT (FilePart && FilePart > DirPart);
  1096. *_tcsdec2 (DirPart, FilePart) = 0;
  1097. //
  1098. // Deleted? Delete the STF line.
  1099. //
  1100. if (rc == CONVERTPATH_DELETED) {
  1101. DEBUGMSG ((DBG_STF, "STF Line %u is no longer needed", StfLine));
  1102. if (!pReplaceDirReferences (TablePtr, StfLine, InstallDestDir)) {
  1103. return FALSE;
  1104. }
  1105. if (!pDeleteStfLine (TablePtr, StfLine)) {
  1106. return FALSE;
  1107. }
  1108. }
  1109. //
  1110. // Moved? Update the STF line.
  1111. //
  1112. else if (rc == CONVERTPATH_REMAPPED) {
  1113. //
  1114. // Has the file name changed? If so, point it to the new location.
  1115. //
  1116. if (!StringIMatch (OrgFilePart, FilePart)) {
  1117. //
  1118. // Update INI file by duplicating the INI line
  1119. //
  1120. // Generate a unique key name
  1121. pGenerateUniqueKeyName (TablePtr, Section, TEXT("WIN9XUPG_"), NewKeyName);
  1122. // Compute size needed
  1123. Size = 0;
  1124. for (Fields = 0 ; InfFields[Fields] ; Fields++) {
  1125. if (Fields != 1) {
  1126. Size += ByteCount (InfFields[Fields]);
  1127. Size += sizeof (TCHAR);
  1128. } else {
  1129. Size += ByteCount (FilePart);
  1130. }
  1131. }
  1132. // Generate the INF line
  1133. NewLine = AllocText (Size);
  1134. if (NewLine) {
  1135. p = NewLine;
  1136. *p = 0;
  1137. for (Fields = 0 ; InfFields[Fields] ; Fields++) {
  1138. if (Fields) {
  1139. p = _tcsappend (p, TEXT(","));
  1140. }
  1141. if (Fields == 1) {
  1142. p = _tcsappend (p, FilePart);
  1143. } else {
  1144. p = _tcsappend (p, InfFields[Fields]);
  1145. }
  1146. }
  1147. // Write the new line
  1148. StfAddInfLineToTable (
  1149. TablePtr,
  1150. Section,
  1151. NewKeyName,
  1152. NewLine,
  1153. LINEFLAG_KEY_QUOTED
  1154. );
  1155. FreeText (NewLine);
  1156. // Update the STF
  1157. FileEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
  1158. MYASSERT (FileEntry);
  1159. OldField = GetTableEntryStr (TablePtr, FileEntry);
  1160. NewLine = AllocText (
  1161. ByteCount (FilePart) +
  1162. ByteCount (OldField) +
  1163. ByteCount (InfSection)
  1164. );
  1165. StringCopy (NewLine, OldField);
  1166. p = _tcschr (NewLine, TEXT(':'));
  1167. if (!p) {
  1168. p = NewLine;
  1169. } else {
  1170. p = _tcsinc (p);
  1171. MYASSERT (*p == TEXT(' '));
  1172. p = _tcsinc (p);
  1173. }
  1174. *p = 0;
  1175. p = _tcsappend (p, InfSection);
  1176. p = _tcsappend (p, TEXT(", "));
  1177. p = _tcsappend (p, NewKeyName);
  1178. // ignore memory failure, it will be picked up below
  1179. ReplaceTableEntryStr (TablePtr, FileEntry, NewLine);
  1180. FreeText (NewLine);
  1181. }
  1182. }
  1183. //
  1184. // Store the directory change in the STF table.
  1185. //
  1186. DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_INSTALL_DESTDIR, NULL);
  1187. AppendWack (DirPart);
  1188. if (!ReplaceTableEntryStr (TablePtr, DataEntry, DirPart)) {
  1189. LOG ((
  1190. LOG_ERROR,
  1191. "Could not update table entry for single command"
  1192. ));
  1193. return FALSE;
  1194. }
  1195. }
  1196. return TRUE;
  1197. }
  1198. PSTFINFSECTION
  1199. pGetNewInfSection (
  1200. IN PSETUPTABLE TablePtr,
  1201. IN PCTSTR FileSpec,
  1202. OUT PBOOL CreatedFlag
  1203. )
  1204. /*++
  1205. Routine Description:
  1206. pGetNewInfSection determines if a section already exists for the specified
  1207. file specification, and if so it returns the pointer to the existing
  1208. section. If the section does not exist, it creates a new section (making
  1209. sure there are no other sections with the same name), and returns a
  1210. pointer to the new section.
  1211. This routine is used by the code that splits one section into several.
  1212. Arguments:
  1213. TablePtr - Specifies the setup table file to process
  1214. FileSpec - Specifies the full file path of the file being processed
  1215. CreatedFlag - Receives TRUE if a new section had to be created
  1216. Return Value:
  1217. A pointer to the INF section in which the file should be added to, or
  1218. NULL if an error occurred.
  1219. --*/
  1220. {
  1221. TCHAR DirName[MAX_TCHAR_PATH];
  1222. TCHAR Node[MEMDB_MAX];
  1223. DWORD SectionNum;
  1224. PTSTR p;
  1225. TCHAR SectionName[64];
  1226. static DWORD SectionSeq = 0;
  1227. *CreatedFlag = FALSE;
  1228. //
  1229. // See if section already exists, and if it does, return the section
  1230. // pointer.
  1231. //
  1232. _tcssafecpy (DirName, FileSpec, MAX_TCHAR_PATH);
  1233. p = _tcsrchr (DirName, TEXT('\\'));
  1234. if (p) {
  1235. *p = 0;
  1236. }
  1237. MemDbBuildKey (Node, MEMDB_CATEGORY_STF_SECTIONS, DirName, NULL, NULL);
  1238. if (MemDbGetValue (Node, &SectionNum)) {
  1239. wsprintf (SectionName, S_SECTIONNAME_SPRINTF, SectionNum);
  1240. return StfFindInfSectionInTable (TablePtr, SectionName);
  1241. }
  1242. //
  1243. // The section does not exist. Find an unused section, write the
  1244. // reference to memdb and return the section pointer.
  1245. //
  1246. while (TRUE) {
  1247. SectionSeq++;
  1248. wsprintf (SectionName, S_SECTIONNAME_SPRINTF, SectionSeq);
  1249. if (!StfFindInfSectionInTable (TablePtr, SectionName)) {
  1250. break;
  1251. }
  1252. }
  1253. *CreatedFlag = TRUE;
  1254. MemDbSetValue (Node, SectionSeq);
  1255. return StfAddInfSectionToTable (TablePtr, SectionName);
  1256. }
  1257. VOID
  1258. pGetFileNameFromInfField (
  1259. OUT PTSTR FileName,
  1260. IN PCTSTR InfField
  1261. )
  1262. /*++
  1263. Routine Description:
  1264. pGetFileNameFromInfField extracts a long file name, enclosed between
  1265. angle-brackets. According to the STF spec, the syntax is shortname<longname>.
  1266. This routine returns longname.
  1267. Arguments:
  1268. FileName - Supplies a MAX_TCHAR_PATH buffer that receives the long file name.
  1269. InfField - Specifies the text from the INF field conforming to the
  1270. shortname<longname> syntax.
  1271. Return Value:
  1272. None.
  1273. --*/
  1274. {
  1275. PTSTR LongName;
  1276. PCTSTR p;
  1277. LongName = _tcschr (InfField, TEXT('<'));
  1278. if (LongName) {
  1279. _tcssafecpy (FileName, _tcsinc (LongName), MAX_TCHAR_PATH);
  1280. LongName = _tcschr (FileName, TEXT('>'));
  1281. if (LongName) {
  1282. *LongName = 0;
  1283. }
  1284. } else {
  1285. p = _tcsrchr (InfField, TEXT('\\'));
  1286. if (!p) {
  1287. p = InfField;
  1288. } else {
  1289. p = _tcsinc (p);
  1290. }
  1291. _tcssafecpy (FileName, p, MAX_TCHAR_PATH);
  1292. }
  1293. }
  1294. BOOL
  1295. pDeleteStfLine (
  1296. IN OUT PSETUPTABLE TablePtr,
  1297. IN UINT StfLine
  1298. )
  1299. /*++
  1300. Routine Description:
  1301. pDeleteStfLine removes an STF line from the table. It first checks to
  1302. see if a destination directory is specified, and if one is, it moves it
  1303. to the next line, unless the next line also has a destination directory.
  1304. Arguments:
  1305. TablePtr - Specifies the setup table file to process
  1306. StfLine - Specifies the STF line to delete
  1307. Return Value:
  1308. TRUE if success, FALSE if failure.
  1309. --*/
  1310. {
  1311. PTABLEENTRY TitleEntry;
  1312. PTABLEENTRY DataEntry;
  1313. BOOL b;
  1314. //
  1315. // We simply replace the command with CreateIniLine, which is harmless
  1316. //
  1317. TitleEntry = GetTableEntry (TablePtr, StfLine, COLUMN_COMMAND, NULL);
  1318. DataEntry = GetTableEntry (TablePtr, StfLine, COLUMN_OBJECT_DATA, NULL);
  1319. if (!TitleEntry || !DataEntry) {
  1320. MYASSERT (FALSE);
  1321. return TRUE;
  1322. }
  1323. b = ReplaceTableEntryStr (TablePtr, TitleEntry, TEXT("CreateIniLine"));
  1324. if (b) {
  1325. b = ReplaceTableEntryStr (
  1326. TablePtr,
  1327. DataEntry,
  1328. TEXT("\"WIN.INI\", \"Old Win9x Setting\", \"DummyKey\", \"unused\"")
  1329. );
  1330. }
  1331. return b;
  1332. #if 0
  1333. PTABLEENTRY NextLineEntry;
  1334. PCTSTR InstallDestDir;
  1335. PCTSTR NextInstallDestDir;
  1336. __try {
  1337. //
  1338. // Test for last line
  1339. //
  1340. if (StfLine + 1 >= TablePtr->LineCount) {
  1341. __leave;
  1342. }
  1343. //
  1344. // Obtain StfLine dest dir (column 10)
  1345. //
  1346. if (!GetTableEntry (TablePtr, StfLine, COLUMN_DEST_DIR, &InstallDestDir)) {
  1347. //
  1348. // StfLine is not valid (unexpected)
  1349. //
  1350. DEBUGMSG ((DBG_STF, "Line %u does not have column 10", StfLine));
  1351. __leave;
  1352. }
  1353. //
  1354. // If no dest dir, do not modify the next line
  1355. //
  1356. if (!InstallDestDir || !InstallDestDir[0]) {
  1357. __leave;
  1358. }
  1359. //
  1360. // Obtain next line's dest dir (column 10)
  1361. //
  1362. NextLineEntry = GetTableEntry (TablePtr, StfLine + 1, COLUMN_DEST_DIR, &NextInstallDestDir);
  1363. if (!NextLineEntry) {
  1364. //
  1365. // Next StfLine is not valid (unexpected)
  1366. //
  1367. DEBUGMSG ((DBG_WHOOPS, "pDeleteStfLine: Next line %u does not have column 10", StfLine+1));
  1368. __leave;
  1369. }
  1370. //
  1371. // If next line's dest dir is not empty, do not modify the line
  1372. //
  1373. if (NextInstallDestDir && NextInstallDestDir[0]) {
  1374. __leave;
  1375. }
  1376. //
  1377. // Now set InstallDestDir on NextLineEntry line
  1378. //
  1379. if (!ReplaceTableEntryStr (TablePtr, NextLineEntry, InstallDestDir)) {
  1380. DEBUGMSG ((
  1381. DBG_ERROR,
  1382. "Cannot replace a destination dir in STF file. "
  1383. "Line=%u, InstallDestDir=%s",
  1384. StfLine + 1,
  1385. InstallDestDir
  1386. ));
  1387. return FALSE;
  1388. }
  1389. }
  1390. __finally {
  1391. }
  1392. return DeleteLineInTable (TablePtr, StfLine);
  1393. #endif
  1394. }
  1395. BOOL
  1396. pReplaceDirReferences (
  1397. IN OUT PSETUPTABLE TablePtr,
  1398. IN UINT StfLine,
  1399. IN PCTSTR DirSpec
  1400. )
  1401. /*++
  1402. Routine Description:
  1403. pReplaceDirReferences scans column 14, looking for all references
  1404. to StfLine, and replaces the reference with DirSpec.
  1405. Arguments:
  1406. TablePtr - Specifies the setup table file to process
  1407. StfLine - Specifies the STF line to substitute
  1408. DirSpec - Specifies the effective directory for the STF line.
  1409. This directory is used if the STF line has to be
  1410. deleted.
  1411. Return Value:
  1412. TRUE if success, FALSE if failure.
  1413. --*/
  1414. {
  1415. UINT Line, Count;
  1416. PTABLEENTRY InstallDestDirEntry;
  1417. PCTSTR InstallDestDir;
  1418. TCHAR NumStr[32];
  1419. UINT NumStrLen;
  1420. PCTSTR AfterPercentNum;
  1421. CHARTYPE c;
  1422. PCTSTR NewInstallDestDir;
  1423. NumStrLen = wsprintf (NumStr, TEXT("%%%u"), StfLine);
  1424. Count = TablePtr->LineCount;
  1425. for (Line = 0 ; Line < Count ; Line++) {
  1426. InstallDestDirEntry = GetTableEntry (TablePtr, Line, COLUMN_DEST_DIR, &InstallDestDir);
  1427. if (!InstallDestDirEntry) {
  1428. continue;
  1429. }
  1430. //
  1431. // Does InstallDestDir have %<n> (where <n> equals StfLine)?
  1432. //
  1433. if (StringIMatchCharCount (InstallDestDir, NumStr, NumStrLen)) {
  1434. //
  1435. // The next character must be a wack or nul
  1436. //
  1437. AfterPercentNum = CharCountToPointer (InstallDestDir, NumStrLen);
  1438. c = (CHARTYPE)_tcsnextc (AfterPercentNum);
  1439. if (c == 0 || c == TEXT('\\')) {
  1440. //
  1441. // Create new dest dir
  1442. //
  1443. if (c) {
  1444. NewInstallDestDir = JoinPaths (DirSpec, _tcsinc (AfterPercentNum));
  1445. } else {
  1446. NewInstallDestDir = DuplicatePathString (DirSpec, 0);
  1447. }
  1448. __try {
  1449. if (!ReplaceTableEntryStr (TablePtr, InstallDestDirEntry, NewInstallDestDir)) {
  1450. LOG ((
  1451. LOG_ERROR,
  1452. "Cannot replace a destination dir in STF file. "
  1453. "Line=%u, NewInstallDestDir=%s",
  1454. Line,
  1455. NewInstallDestDir
  1456. ));
  1457. return FALSE;
  1458. }
  1459. ELSE_DEBUGMSG ((
  1460. DBG_STF,
  1461. "Line %u: Dest dir %s replaced with %s",
  1462. Line,
  1463. InstallDestDir,
  1464. NewInstallDestDir
  1465. ));
  1466. }
  1467. __finally {
  1468. FreePathString (NewInstallDestDir);
  1469. }
  1470. }
  1471. }
  1472. }
  1473. return TRUE;
  1474. }
  1475. BOOL
  1476. pCreateNewStfLine (
  1477. IN OUT PSETUPTABLE TablePtr,
  1478. IN UINT StfLine,
  1479. IN PCTSTR ObjectData,
  1480. IN PCTSTR InstallDestDir
  1481. )
  1482. /*++
  1483. Routine Description:
  1484. pCreateNewStfLine inserts a new line immediately following
  1485. StfLine, using the maximum object number. It copies the
  1486. STF line specified and modifies the ObjectData (column 5)
  1487. and InstallDestDir (column 14).
  1488. Arguments:
  1489. TablePtr - Specifies the setup table file to process
  1490. StfLine - Specifies the prototype STF line
  1491. ObjectData - Specifies the replacement for the ObjectData
  1492. column
  1493. InstallDestDir - Specifies the replacement for the DestDir
  1494. column
  1495. Return Value:
  1496. TRUE if success, FALSE if failure.
  1497. --*/
  1498. {
  1499. UINT NewLine;
  1500. UINT NewObj;
  1501. PTABLEENTRY CopyEntry, NewEntry;
  1502. UINT Col;
  1503. TCHAR Buf[32], ReplaceBuf[32];
  1504. //
  1505. // Copy StfLine to StfLine+1, updating fields as necessary
  1506. //
  1507. NewLine = StfLine + 1;
  1508. TablePtr->MaxObj++;
  1509. NewObj = TablePtr->MaxObj;
  1510. if (!InsertEmptyLineInTable (TablePtr, NewLine)) {
  1511. LOG ((LOG_ERROR, "Unable to insert new line in STF table"));
  1512. return FALSE;
  1513. }
  1514. Col = 0;
  1515. while (TRUE) {
  1516. CopyEntry = GetTableEntry (TablePtr, StfLine, Col, NULL);
  1517. if (!CopyEntry) {
  1518. break;
  1519. }
  1520. if (!AppendTableEntry (TablePtr, NewLine, CopyEntry)) {
  1521. LOG ((LOG_ERROR, "Unable to append all entries to line"));
  1522. return FALSE;
  1523. }
  1524. NewEntry = GetTableEntry (TablePtr, NewLine, Col, NULL);
  1525. MYASSERT(NewEntry);
  1526. if (!NewEntry) {
  1527. return FALSE;
  1528. }
  1529. if (Col == 0) {
  1530. wsprintf (Buf, TEXT("%u"), NewObj);
  1531. if (!ReplaceTableEntryStr (TablePtr, NewEntry, Buf)) {
  1532. LOG ((LOG_ERROR, "Unable to replace ObjID on line"));
  1533. return FALSE;
  1534. }
  1535. } else if (Col == COLUMN_OBJECT_DATA) {
  1536. if (!ReplaceTableEntryStr (TablePtr, NewEntry, ObjectData)) {
  1537. LOG ((LOG_ERROR, "Unable to replace ObjectData on line"));
  1538. return FALSE;
  1539. }
  1540. } else if (Col == COLUMN_INSTALL_DESTDIR) {
  1541. if (!ReplaceTableEntryStr (TablePtr, NewEntry, InstallDestDir)) {
  1542. LOG ((LOG_ERROR, "Unable to replace ObjectData on line"));
  1543. return FALSE;
  1544. }
  1545. }
  1546. Col++;
  1547. }
  1548. //
  1549. // Find all lines with references to StfLine and add in NewLine
  1550. //
  1551. wsprintf (Buf, TEXT("%u"), StfLine);
  1552. wsprintf (ReplaceBuf, TEXT("%u %u"), StfLine, NewLine);
  1553. return pSearchAndReplaceObjectRefs (TablePtr, Buf, ReplaceBuf);
  1554. }
  1555. BOOL
  1556. pSearchAndReplaceObjectRefs (
  1557. IN OUT PSETUPTABLE TablePtr,
  1558. IN PCTSTR SrcStr,
  1559. IN PCTSTR DestStr
  1560. )
  1561. /*++
  1562. Routine Description:
  1563. pSearchAndReplaceObjectRefs scans column 5 of the setup table,
  1564. looking for any occurance of SrcStr and replacing it with
  1565. DestStr.
  1566. Arguments:
  1567. TablePtr - Specifies the STF table to process
  1568. SrcStr - Specifies the string to locate and replace
  1569. DestStr - Specifies the replacement string
  1570. Return Value:
  1571. TRUE if the STF file was converted, FALSE if failure.
  1572. --*/
  1573. {
  1574. UINT Line;
  1575. UINT Count;
  1576. PTABLEENTRY Entry;
  1577. PCTSTR LineString;
  1578. PCTSTR UpdatedString;
  1579. Count = TablePtr->LineCount;
  1580. for (Line = 0 ; Line < Count ; Line++) {
  1581. Entry = GetTableEntry (TablePtr, Line, COLUMN_OBJECT_DATA, &LineString);
  1582. if (!Entry || !LineString || !LineString[0]) {
  1583. continue;
  1584. }
  1585. UpdatedString = StringSearchAndReplace (LineString, SrcStr, DestStr);
  1586. if (UpdatedString) {
  1587. __try {
  1588. if (!ReplaceTableEntryStr (TablePtr, Entry, UpdatedString)) {
  1589. LOG ((LOG_ERROR, "Unable to replace text on line"));
  1590. return FALSE;
  1591. }
  1592. }
  1593. __finally {
  1594. FreePathString (UpdatedString);
  1595. }
  1596. }
  1597. }
  1598. return TRUE;
  1599. }
  1600. BOOL
  1601. pProcessSetupTableFile (
  1602. IN PCTSTR StfFileSpec
  1603. )
  1604. /*++
  1605. Routine Description:
  1606. pProcessSetupTableFile performs all processing on the file specified.
  1607. Here are the steps involved in converting an STF file:
  1608. - Determine the associated INF
  1609. - Prepare the SETUPTABLE structure
  1610. - Scan the table for file-based actions
  1611. - Convert file paths used by the actions
  1612. - Convert group references when STF lines are split
  1613. - Write the modifications to disk
  1614. - Replace the original INF and STF with the new versions
  1615. Arguments:
  1616. StfFileSpec - Specifies the full file path to the STF file needing processing.
  1617. The associated INF must be in the same directory as the STF
  1618. file referencing it.
  1619. Return Value:
  1620. TRUE if the STF file was converted, FALSE if failure.
  1621. --*/
  1622. {
  1623. SETUPTABLE Table;
  1624. DWORD StfAttribs, InfAttribs;
  1625. BOOL b;
  1626. TCHAR SourceStf[MAX_TCHAR_PATH];
  1627. TCHAR DestStf[MAX_TCHAR_PATH];
  1628. TCHAR SourceInf[MAX_TCHAR_PATH];
  1629. TCHAR DestInf[MAX_TCHAR_PATH];
  1630. if (!CreateSetupTable (StfFileSpec, &Table)) {
  1631. DEBUGMSG ((DBG_STF, "ProcessSetupTableFile: Error parsing file %s.", StfFileSpec));
  1632. return FALSE;
  1633. }
  1634. __try {
  1635. //
  1636. // Process the table
  1637. //
  1638. if (!pProcessSetupTable (&Table)) {
  1639. DEBUGMSG ((DBG_STF, "ProcessSetupTableFile: Error processing table for %s.", StfFileSpec));
  1640. return FALSE;
  1641. }
  1642. //
  1643. // Write changes to temporary files
  1644. //
  1645. if (!WriteSetupTable (&Table)) {
  1646. LOG ((LOG_ERROR, "Cannot write setup table for %s.", StfFileSpec));
  1647. return FALSE;
  1648. }
  1649. //
  1650. // Copy paths before we destroy the table struct
  1651. //
  1652. _tcssafecpy (SourceStf, Table.SourceStfFileSpec, MAX_TCHAR_PATH);
  1653. _tcssafecpy (DestStf, Table.DestStfFileSpec, MAX_TCHAR_PATH);
  1654. _tcssafecpy (SourceInf, Table.SourceInfFileSpec, MAX_TCHAR_PATH);
  1655. if (Table.DestInfFileSpec) {
  1656. _tcssafecpy (DestInf, Table.DestInfFileSpec, MAX_TCHAR_PATH);
  1657. } else {
  1658. *DestInf = 0;
  1659. }
  1660. }
  1661. __finally {
  1662. DestroySetupTable (&Table);
  1663. }
  1664. //
  1665. // Replace the original files with temporary files
  1666. //
  1667. StfAttribs = GetFileAttributes (SourceStf);
  1668. if (StfAttribs != 0xffffffff) {
  1669. LONG rc;
  1670. SetFileAttributes (SourceStf, FILE_ATTRIBUTE_NORMAL);
  1671. DeleteFile (SourceStf);
  1672. rc = GetLastError();
  1673. b = OurMoveFile (DestStf, SourceStf);
  1674. if (!b) {
  1675. return FALSE;
  1676. }
  1677. SetFileAttributes (SourceStf, StfAttribs);
  1678. }
  1679. InfAttribs = GetFileAttributes (SourceInf);
  1680. if (InfAttribs != 0xffffffff && *DestInf) {
  1681. SetFileAttributes (SourceInf, FILE_ATTRIBUTE_NORMAL);
  1682. DeleteFile (SourceInf);
  1683. b = OurMoveFile (DestInf, SourceInf);
  1684. if (!b) {
  1685. return FALSE;
  1686. }
  1687. SetFileAttributes (SourceInf, InfAttribs);
  1688. }
  1689. return TRUE;
  1690. }