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.

1714 lines
43 KiB

  1. /*++
  2. Copyright (c) 1993-1998 Microsoft Corporation
  3. Module Name:
  4. mru.c
  5. Abstract:
  6. Implementation of source list handling routines.
  7. Author:
  8. Ted Miller (tedm) 30-Aug-1995
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. //
  14. // Location in registry where per-system MRU list is stored
  15. // (relative to HKEY_LOCAL_MACHINE).
  16. //
  17. PCTSTR pszPerSystemKey = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup");
  18. PCTSTR pszPerSystemVal = TEXT("Installation Sources");
  19. //
  20. // Location in registry where per-user MRU list is stored.
  21. // (relative to HKEY_CURRENT_USER).
  22. //
  23. PCTSTR pszPerUserKey = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup");
  24. PCTSTR pszPerUserVal = TEXT("Installation Sources");
  25. typedef PTSTR *APTSTR;
  26. //
  27. // Platform strings we recognize.
  28. //
  29. PCTSTR PlatformPathComponents[] = { TEXT("\\alpha"),
  30. TEXT("\\nec98"), //NEC98
  31. TEXT("\\i386"),
  32. TEXT("\\x86"),
  33. TEXT("\\mips"),
  34. TEXT("\\ppc"),
  35. TEXT("\\axp64"),
  36. TEXT("\\ia64"),
  37. NULL
  38. };
  39. //
  40. // These are guarded by MruCritSect.
  41. //
  42. PTSTR *TemporarySourceList;
  43. UINT TemporarySourceCount;
  44. BOOL MruNoBrowse;
  45. VOID
  46. pSetupStripTrailingPlatformComponent(
  47. IN OUT PTSTR *Paths,
  48. IN OUT PDWORD NumPaths
  49. );
  50. BOOL LockMruCritSect()
  51. {
  52. BOOL locked = FALSE;
  53. try {
  54. EnterCriticalSection(&MruCritSect);
  55. locked = TRUE;
  56. } except (EXCEPTION_EXECUTE_HANDLER) {
  57. }
  58. if(!locked) {
  59. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  60. }
  61. return locked;
  62. }
  63. BOOL
  64. _SetupSetSourceList(
  65. IN DWORD Flags,
  66. IN PCTSTR *SourceList,
  67. IN UINT SourceCount
  68. )
  69. /*++
  70. Routine Description:
  71. This routine allows the caller to set the list of installation
  72. sources for either the current user or the system (common to
  73. all users).
  74. Arguments:
  75. Flags - a combination of the following values:
  76. SRCLIST_SYSTEM - specify that the list is to become the
  77. per-system list. The caller must be administrator.
  78. SRCLIST_USER - specify that the list is to become the per-user
  79. list.
  80. SRCLIST_TEMPORARY - specify that the list is to become the
  81. entire list for the duration of the current process,
  82. or until this routine is called again to change the behavior.
  83. Exactly one of SRCLIST_SYSTEM, SRCLIST_USER, and SRCLIST_TEMPORARY
  84. must be specified.
  85. SRCLIST_NOBROWSE - specify that the user is not allowed to add
  86. or change sources when the SetupPromptForDisk API is used.
  87. Typically used in combination with SRCLIST_TEMPORARY.
  88. SourceList - supplies array of strings that are to become the
  89. source list, as described by the Flags parameter.
  90. SourceCount - specifies number of elements in the SourceList array.
  91. Return Value:
  92. --*/
  93. {
  94. DWORD flags;
  95. DWORD d;
  96. UINT u,v;
  97. //
  98. // Check flags. Only one of system, user, or temporary may be set.
  99. //
  100. flags = Flags & (SRCLIST_SYSTEM | SRCLIST_USER | SRCLIST_TEMPORARY);
  101. if((flags != SRCLIST_SYSTEM) && (flags != SRCLIST_USER) && (flags != SRCLIST_TEMPORARY)) {
  102. SetLastError(ERROR_INVALID_PARAMETER);
  103. return(FALSE);
  104. }
  105. //
  106. // User must be admin for system flag to work.
  107. //
  108. if((flags == SRCLIST_SYSTEM) && !pSetupIsUserAdmin()) {
  109. SetLastError(ERROR_ACCESS_DENIED);
  110. return(FALSE);
  111. }
  112. //
  113. // Only allow one thread at a time in this process to access
  114. // the temporary source list.
  115. //
  116. if(!LockMruCritSect()) {
  117. return FALSE;
  118. }
  119. if(Flags & SRCLIST_NOBROWSE) {
  120. MruNoBrowse = TRUE;
  121. }
  122. d = NO_ERROR;
  123. if(flags == SRCLIST_TEMPORARY) {
  124. if(TemporarySourceList) {
  125. SetupFreeSourceList(&TemporarySourceList,TemporarySourceCount);
  126. }
  127. //
  128. // Duplicate the list the caller passed in.
  129. //
  130. if(TemporarySourceList = MyMalloc(SourceCount * sizeof(PTSTR))) {
  131. TemporarySourceCount = SourceCount;
  132. for(u=0; u<SourceCount; u++) {
  133. TemporarySourceList[u] = DuplicateString(SourceList[u]);
  134. if(!TemporarySourceList[u]) {
  135. for(v=0; v<u; v++) {
  136. MyFree(TemporarySourceList[v]);
  137. }
  138. MyFree(TemporarySourceList);
  139. TemporarySourceList = NULL;
  140. TemporarySourceCount = 0;
  141. d = ERROR_NOT_ENOUGH_MEMORY;
  142. break;
  143. }
  144. }
  145. } else {
  146. d = ERROR_NOT_ENOUGH_MEMORY;
  147. }
  148. } else {
  149. //
  150. // User or system.
  151. //
  152. d = pSetupSetArrayToMultiSzValue(
  153. (flags == SRCLIST_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
  154. (flags == SRCLIST_SYSTEM) ? pszPerSystemKey : pszPerUserKey,
  155. (flags == SRCLIST_SYSTEM) ? pszPerSystemVal : pszPerUserVal,
  156. (PTSTR *)SourceList,
  157. SourceCount
  158. );
  159. }
  160. //
  161. // Done with protected resource
  162. //
  163. LeaveCriticalSection(&MruCritSect);
  164. SetLastError(d);
  165. return(d == NO_ERROR);
  166. }
  167. #ifdef UNICODE
  168. //
  169. // ANSI version
  170. //
  171. BOOL
  172. SetupSetSourceListA(
  173. IN DWORD Flags,
  174. IN PCSTR *SourceList,
  175. IN UINT SourceCount
  176. )
  177. {
  178. PCWSTR *sourceList;
  179. UINT u;
  180. DWORD rc;
  181. BOOL b;
  182. sourceList = MyMalloc(SourceCount*sizeof(PCWSTR));
  183. if(!sourceList) {
  184. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  185. return(FALSE);
  186. }
  187. ZeroMemory((PVOID)sourceList,SourceCount*sizeof(PCWSTR));
  188. rc = NO_ERROR;
  189. for(u=0; (rc==NO_ERROR) && (u<SourceCount); u++) {
  190. //
  191. // Try/except guards access to SourceList[u] in case
  192. // SourceList is a bad pointer
  193. //
  194. try {
  195. rc = pSetupCaptureAndConvertAnsiArg(SourceList[u],&sourceList[u]);
  196. } except(EXCEPTION_EXECUTE_HANDLER) {
  197. rc = ERROR_INVALID_PARAMETER;
  198. }
  199. }
  200. if(rc == NO_ERROR) {
  201. b = _SetupSetSourceList(Flags,sourceList,SourceCount);
  202. rc = GetLastError();
  203. } else {
  204. b = FALSE;
  205. }
  206. for(u=0; u<SourceCount; u++) {
  207. if(sourceList[u]) {
  208. MyFree(sourceList[u]);
  209. }
  210. }
  211. MyFree(sourceList);
  212. SetLastError(rc);
  213. return(b);
  214. }
  215. #else
  216. //
  217. // Unicode stub
  218. //
  219. BOOL
  220. SetupSetSourceListW(
  221. IN DWORD Flags,
  222. IN PCWSTR *SourceList,
  223. IN UINT SourceCount
  224. )
  225. {
  226. UNREFERENCED_PARAMETER(Flags);
  227. UNREFERENCED_PARAMETER(SourceList);
  228. UNREFERENCED_PARAMETER(SourceCount);
  229. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  230. return(FALSE);
  231. }
  232. #endif
  233. BOOL
  234. SetupSetSourceList(
  235. IN DWORD Flags,
  236. IN PCTSTR *SourceList,
  237. IN UINT SourceCount
  238. )
  239. {
  240. PCTSTR *sourceList;
  241. UINT u;
  242. DWORD rc;
  243. BOOL b;
  244. sourceList = MyMalloc(SourceCount*sizeof(PCTSTR));
  245. if(!sourceList) {
  246. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  247. return(FALSE);
  248. }
  249. ZeroMemory((PVOID)sourceList,SourceCount*sizeof(PCTSTR));
  250. rc = NO_ERROR;
  251. for(u=0; (rc==NO_ERROR) && (u<SourceCount); u++) {
  252. //
  253. // Try/except guards access to SourceList[u] in case
  254. // SourceList is a bad pointer
  255. //
  256. try {
  257. rc = CaptureStringArg(SourceList[u],&sourceList[u]);
  258. } except(EXCEPTION_EXECUTE_HANDLER) {
  259. rc = ERROR_INVALID_PARAMETER;
  260. }
  261. }
  262. if(rc == NO_ERROR) {
  263. b = _SetupSetSourceList(Flags,sourceList,SourceCount);
  264. rc = GetLastError();
  265. } else {
  266. b = FALSE;
  267. }
  268. for(u=0; u<SourceCount; u++) {
  269. if(sourceList[u]) {
  270. MyFree(sourceList[u]);
  271. }
  272. }
  273. MyFree(sourceList);
  274. SetLastError(rc);
  275. return(b);
  276. }
  277. BOOL
  278. SetupCancelTemporarySourceList(
  279. VOID
  280. )
  281. /*++
  282. Routine Description:
  283. This routine cancels any temporary list and no-browse behavior
  284. and reverts to standard list behavior.
  285. Arguments:
  286. None.
  287. Return Value:
  288. TRUE if a temporary list was in effect; FALSE if otherwise.
  289. --*/
  290. {
  291. BOOL b;
  292. if(!LockMruCritSect()) {
  293. return FALSE;
  294. }
  295. MruNoBrowse = FALSE;
  296. if(TemporarySourceList) {
  297. //
  298. // SetupFreeSourceList zeros out the pointer for us.
  299. //
  300. SetupFreeSourceList(&TemporarySourceList,TemporarySourceCount);
  301. TemporarySourceCount = 0;
  302. b = TRUE;
  303. } else {
  304. b = FALSE;
  305. }
  306. LeaveCriticalSection(&MruCritSect);
  307. return(b);
  308. }
  309. BOOL
  310. _SetupAddToSourceList(
  311. IN DWORD Flags,
  312. IN PCTSTR Source
  313. )
  314. /*++
  315. Routine Description:
  316. This routine allows the caller to append a value to the list
  317. of installation sources for either the current user or the system.
  318. If the value already exists it is removed first.
  319. Arguments:
  320. Flags - a combination of the following values:
  321. SRCLIST_SYSTEM - specify that the source is to added to the
  322. per-system list. The caller must be administrator.
  323. SRCLIST_USER - specify that the list is to be added to the per-user
  324. list.
  325. SRCLIST_SYSIFADMIN - specifies that if the caller is administrator,
  326. then the source is added to the system list; if the caller
  327. is not administrator then the source is added to the per-user
  328. list for the current user.
  329. If a temporary list is currently in use (see SetupSetSourceList),
  330. these 3 flags are ignored and the source is added to the temporary list.
  331. SRCLIST_APPEND - specify that the source is to be added to the end
  332. of the given list. Otherwise it is added to the beginning.
  333. Source - specifies the source to be added to the list.
  334. Return Value:
  335. --*/
  336. {
  337. APTSTR Lists[2];
  338. UINT Counts[2];
  339. UINT NumberOfLists;
  340. DWORD d;
  341. UINT u;
  342. PTSTR p;
  343. PVOID pTmp;
  344. HKEY RootKeys[2];
  345. PCTSTR SubKeys[2];
  346. PCTSTR Vals[2];
  347. BOOL NeedToFree[2];
  348. if(!LockMruCritSect()) {
  349. return FALSE;
  350. }
  351. //
  352. // Remove first, if present. This makes things easier for us later.
  353. // Do this inside the locks to ensure atomicity for the add call as
  354. // a whole.
  355. //
  356. if(!SetupRemoveFromSourceList(Flags,Source)) {
  357. d = GetLastError();
  358. LeaveCriticalSection(&MruCritSect);
  359. SetLastError(d);
  360. return(FALSE);
  361. }
  362. //
  363. // Check Temporary list first.
  364. //
  365. d = NO_ERROR;
  366. if(TemporarySourceList) {
  367. Lists[0] = TemporarySourceList;
  368. Counts[0] = TemporarySourceCount;
  369. NumberOfLists = 1;
  370. NeedToFree[0] = FALSE;
  371. } else {
  372. //
  373. // Check sysifadmin flag and turn on appropriate flag.
  374. //
  375. if(Flags & SRCLIST_SYSIFADMIN) {
  376. Flags |= pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
  377. }
  378. NumberOfLists = 0;
  379. if(Flags & SRCLIST_SYSTEM) {
  380. if(pSetupIsUserAdmin()) {
  381. d = pSetupQueryMultiSzValueToArray(
  382. HKEY_LOCAL_MACHINE,
  383. pszPerSystemKey,
  384. pszPerSystemVal,
  385. &Lists[0],
  386. &Counts[0],
  387. FALSE
  388. );
  389. if(d == NO_ERROR) {
  390. NumberOfLists = 1;
  391. RootKeys[0] = HKEY_LOCAL_MACHINE;
  392. SubKeys[0] = pszPerSystemKey;
  393. Vals[0] = pszPerSystemVal;
  394. NeedToFree[0] = TRUE;
  395. } else {
  396. Lists[0] = NULL;
  397. }
  398. } else {
  399. d = ERROR_ACCESS_DENIED;
  400. }
  401. }
  402. if((Flags & SRCLIST_USER) && (d == NO_ERROR)) {
  403. d = pSetupQueryMultiSzValueToArray(
  404. HKEY_CURRENT_USER,
  405. pszPerSystemKey,
  406. pszPerSystemVal,
  407. &Lists[NumberOfLists],
  408. &Counts[NumberOfLists],
  409. FALSE
  410. );
  411. if(d == NO_ERROR) {
  412. RootKeys[NumberOfLists] = HKEY_CURRENT_USER;
  413. SubKeys[NumberOfLists] = pszPerUserKey;
  414. Vals[NumberOfLists] = pszPerUserVal;
  415. NeedToFree[NumberOfLists] = TRUE;
  416. NumberOfLists++;
  417. } else {
  418. Lists[NumberOfLists] = NULL;
  419. }
  420. }
  421. }
  422. if(d == NO_ERROR) {
  423. //
  424. // Do each list.
  425. //
  426. for(u=0; (d==NO_ERROR) && (u<NumberOfLists); u++) {
  427. if(p = DuplicateString(Source)) {
  428. if(pTmp = MyRealloc(Lists[u],(Counts[u]+1)*sizeof(PTSTR))) {
  429. Lists[u] = pTmp;
  430. if(Flags & SRCLIST_APPEND) {
  431. Lists[u][Counts[u]] = p;
  432. } else {
  433. MoveMemory(&Lists[u][1],Lists[u],Counts[u] * sizeof(PTSTR));
  434. Lists[u][0] = p;
  435. }
  436. Counts[u]++;
  437. //
  438. // Put back in registry if necessary.
  439. //
  440. if(TemporarySourceList) {
  441. TemporarySourceList = Lists[u];
  442. TemporarySourceCount = Counts[0];
  443. } else {
  444. d = pSetupSetArrayToMultiSzValue(
  445. RootKeys[u],
  446. SubKeys[u],
  447. Vals[u],
  448. Lists[u],
  449. Counts[u]
  450. );
  451. if(NeedToFree[u]) {
  452. SetupFreeSourceList(&Lists[u],Counts[u]);
  453. }
  454. }
  455. } else {
  456. d = ERROR_NOT_ENOUGH_MEMORY;
  457. }
  458. } else {
  459. d = ERROR_NOT_ENOUGH_MEMORY;
  460. }
  461. }
  462. }
  463. //
  464. // Done looking at temporary list.
  465. //
  466. //
  467. LeaveCriticalSection(&MruCritSect);
  468. SetLastError(d);
  469. return(d == NO_ERROR);
  470. }
  471. #ifdef UNICODE
  472. //
  473. // ANSI version
  474. //
  475. BOOL
  476. SetupAddToSourceListA(
  477. IN DWORD Flags,
  478. IN PCSTR Source
  479. )
  480. {
  481. BOOL b;
  482. DWORD rc;
  483. PCWSTR source;
  484. rc = pSetupCaptureAndConvertAnsiArg(Source,&source);
  485. if(rc == NO_ERROR) {
  486. b = _SetupAddToSourceList(Flags,source);
  487. rc = GetLastError();
  488. MyFree(source);
  489. } else {
  490. b = FALSE;
  491. }
  492. SetLastError(rc);
  493. return(b);
  494. }
  495. #else
  496. //
  497. // Unicode stub
  498. //
  499. BOOL
  500. SetupAddToSourceListW(
  501. IN DWORD Flags,
  502. IN PCWSTR Source
  503. )
  504. {
  505. UNREFERENCED_PARAMETER(Flags);
  506. UNREFERENCED_PARAMETER(Source);
  507. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  508. return(FALSE);
  509. }
  510. #endif
  511. BOOL
  512. SetupAddToSourceList(
  513. IN DWORD Flags,
  514. IN PCTSTR Source
  515. )
  516. {
  517. BOOL b;
  518. DWORD rc;
  519. PCTSTR source;
  520. rc = CaptureStringArg(Source,&source);
  521. if(rc == NO_ERROR) {
  522. b = _SetupAddToSourceList(Flags,source);
  523. rc = GetLastError();
  524. MyFree(source);
  525. } else {
  526. b = FALSE;
  527. }
  528. SetLastError(rc);
  529. return(b);
  530. }
  531. BOOL
  532. _SetupRemoveFromSourceList(
  533. IN DWORD Flags,
  534. IN PCTSTR Source
  535. )
  536. /*++
  537. Routine Description:
  538. This routine allows the caller to remove a value from the list
  539. of installation sources for either the current user or the system.
  540. The system and user lists are merged at run time.
  541. Arguments:
  542. Flags - a combination of the following values:
  543. SRCLIST_SYSTEM - specify that the source is to removed from the
  544. per-system list. The caller must be administrator.
  545. SRCLIST_USER - specify that the list is to be removed from the
  546. per-user list.
  547. SRCLIST_SYSIFADMIN - specifies that if the caller is administrator,
  548. then the source is removed from the system list; if the caller
  549. is not administrator then the source is removed from the per-user
  550. list for the current user.
  551. Any combination of these flags may be specified on a single call.
  552. If a temporary list is currently in use (see SetupSetSourceList),
  553. these 3 flags are ignored and the source is removed from the temporary list.
  554. SRCLIST_SUBDIRS - specify that all subdirectories of Source are also
  555. to be removed. The determination of subdirectories is done based on
  556. a simple prefix scan.
  557. Source - specifies the source to be removed from the list.
  558. Return Value:
  559. --*/
  560. {
  561. APTSTR Lists[2];
  562. UINT Counts[2];
  563. UINT NumberOfLists;
  564. DWORD d;
  565. BOOL NeedToFree;
  566. UINT u,v;
  567. PTSTR p;
  568. BOOL Match;
  569. UINT Len;
  570. PVOID pTmp;
  571. p = DuplicateString(Source);
  572. if(!p) {
  573. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  574. return(FALSE);
  575. }
  576. CharUpper(p);
  577. Len = lstrlen(p);
  578. if(!LockMruCritSect()) {
  579. MyFree(p);
  580. return FALSE;
  581. }
  582. //
  583. // Check Temporary list first.
  584. //
  585. d = NO_ERROR;
  586. if(TemporarySourceList) {
  587. Lists[0] = TemporarySourceList;
  588. Counts[0] = TemporarySourceCount;
  589. NumberOfLists = 1;
  590. NeedToFree = FALSE;
  591. } else {
  592. //
  593. // Check sysifadmin flag and turn on appropriate flag.
  594. //
  595. if(Flags & SRCLIST_SYSIFADMIN) {
  596. Flags |= pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
  597. }
  598. NeedToFree = TRUE;
  599. NumberOfLists = 0;
  600. if(Flags & SRCLIST_SYSTEM) {
  601. if(pSetupIsUserAdmin()) {
  602. d = pSetupQueryMultiSzValueToArray(
  603. HKEY_LOCAL_MACHINE,
  604. pszPerSystemKey,
  605. pszPerSystemVal,
  606. &Lists[0],
  607. &Counts[0],
  608. FALSE
  609. );
  610. if(d == NO_ERROR) {
  611. NumberOfLists = 1;
  612. } else {
  613. Lists[0] = NULL;
  614. }
  615. } else {
  616. d = ERROR_ACCESS_DENIED;
  617. }
  618. }
  619. if((Flags & SRCLIST_USER) && (d == NO_ERROR)) {
  620. d = pSetupQueryMultiSzValueToArray(
  621. HKEY_CURRENT_USER,
  622. pszPerSystemKey,
  623. pszPerSystemVal,
  624. &Lists[NumberOfLists],
  625. &Counts[NumberOfLists],
  626. FALSE
  627. );
  628. if(d == NO_ERROR) {
  629. NumberOfLists++;
  630. } else {
  631. Lists[NumberOfLists] = NULL;
  632. }
  633. }
  634. }
  635. if(d == NO_ERROR) {
  636. //
  637. // Go through each list.
  638. //
  639. for(u=0; u<NumberOfLists; u++) {
  640. //
  641. // Go though each item in the current list.
  642. //
  643. for(v=0; v<Counts[u]; v++) {
  644. CharUpper(Lists[u][v]);
  645. //
  646. // See if this item matches the one being deleted.
  647. //
  648. Match = FALSE;
  649. if(Flags & SRCLIST_SUBDIRS) {
  650. //
  651. // See if the source the caller passed in is
  652. // a prefix of the source in the list.
  653. //
  654. Match = (_tcsncmp(Lists[u][v],p,Len) == 0);
  655. } else {
  656. Match = (lstrcmp(Lists[u][v],p) == 0);
  657. }
  658. if(Match) {
  659. //
  660. // Need to remove this item.
  661. //
  662. MyFree(Lists[u][v]);
  663. MoveMemory(
  664. &Lists[u][v],
  665. &Lists[u][v+1],
  666. (Counts[u] - (v+1)) * sizeof(PTSTR)
  667. );
  668. Counts[u]--;
  669. v--;
  670. }
  671. }
  672. }
  673. if(TemporarySourceList) {
  674. //
  675. // Shrink temporary source list down to new size.
  676. // Since we're shrinking we don't expect the realloc to fail
  677. // but it's not an error if it does.
  678. //
  679. if(pTmp = MyRealloc(Lists[0],Counts[0]*sizeof(PTSTR))) {
  680. TemporarySourceList = pTmp;
  681. }
  682. TemporarySourceCount = Counts[0];
  683. } else {
  684. //
  685. // Need to put stuff back in registry.
  686. //
  687. u=0;
  688. if(Flags & SRCLIST_SYSTEM) {
  689. d = pSetupSetArrayToMultiSzValue(
  690. HKEY_LOCAL_MACHINE,
  691. pszPerSystemKey,
  692. pszPerSystemVal,
  693. Lists[0],
  694. Counts[0]
  695. );
  696. u++;
  697. }
  698. if((d == NO_ERROR) && (Flags & SRCLIST_USER)) {
  699. d = pSetupSetArrayToMultiSzValue(
  700. HKEY_CURRENT_USER,
  701. pszPerUserKey,
  702. pszPerUserVal,
  703. Lists[u],
  704. Counts[u]
  705. );
  706. u++;
  707. }
  708. }
  709. }
  710. //
  711. // Done looking at temporary list.
  712. //
  713. //
  714. LeaveCriticalSection(&MruCritSect);
  715. if(NeedToFree) {
  716. for(u=0; u<NumberOfLists; u++) {
  717. if(Lists[u]) {
  718. SetupFreeSourceList(&Lists[u],Counts[u]);
  719. }
  720. }
  721. }
  722. MyFree(p);
  723. SetLastError(d);
  724. return(d == NO_ERROR);
  725. }
  726. #ifdef UNICODE
  727. //
  728. // ANSI version
  729. //
  730. BOOL
  731. SetupRemoveFromSourceListA(
  732. IN DWORD Flags,
  733. IN PCSTR Source
  734. )
  735. {
  736. PCWSTR source;
  737. BOOL b;
  738. DWORD rc;
  739. rc = pSetupCaptureAndConvertAnsiArg(Source,&source);
  740. if(rc == NO_ERROR) {
  741. b = _SetupRemoveFromSourceList(Flags,source);
  742. rc = GetLastError();
  743. MyFree(source);
  744. } else {
  745. b = FALSE;
  746. }
  747. SetLastError(rc);
  748. return(b);
  749. }
  750. #else
  751. //
  752. // Unicode stub
  753. //
  754. BOOL
  755. SetupRemoveFromSourceListW(
  756. IN DWORD Flags,
  757. IN PCWSTR Source
  758. )
  759. {
  760. UNREFERENCED_PARAMETER(Flags);
  761. UNREFERENCED_PARAMETER(Source);
  762. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  763. return(FALSE);
  764. }
  765. #endif
  766. BOOL
  767. SetupRemoveFromSourceList(
  768. IN DWORD Flags,
  769. IN PCTSTR Source
  770. )
  771. {
  772. PCTSTR source;
  773. BOOL b;
  774. DWORD rc;
  775. rc = CaptureStringArg(Source,&source);
  776. if(rc == NO_ERROR) {
  777. b = _SetupRemoveFromSourceList(Flags,source);
  778. rc = GetLastError();
  779. MyFree(source);
  780. } else {
  781. b = FALSE;
  782. }
  783. SetLastError(rc);
  784. return(b);
  785. }
  786. #ifdef UNICODE
  787. //
  788. // ANSI version
  789. //
  790. BOOL
  791. SetupQuerySourceListA(
  792. IN DWORD Flags,
  793. OUT PCSTR **List,
  794. OUT PUINT Count
  795. )
  796. {
  797. PCWSTR *list;
  798. UINT count;
  799. BOOL b;
  800. DWORD d;
  801. PSTR *ansilist;
  802. UINT i;
  803. b = SetupQuerySourceListW(Flags,&list,&count);
  804. d = GetLastError();
  805. if(b) {
  806. if(ansilist = MyMalloc(count * sizeof(PCSTR))) {
  807. ZeroMemory(ansilist,count*sizeof(PCSTR));
  808. for(i=0; i<count; i++) {
  809. ansilist[i] = pSetupUnicodeToAnsi(list[i]);
  810. if(!ansilist[i]) {
  811. SetupFreeSourceListA(&ansilist,count);
  812. d = ERROR_NOT_ENOUGH_MEMORY;
  813. b = FALSE;
  814. break;
  815. }
  816. }
  817. if(b) {
  818. //
  819. // Everything's ok, set up caller's out params.
  820. //
  821. try {
  822. *Count = count;
  823. *List = ansilist;
  824. } except(EXCEPTION_EXECUTE_HANDLER) {
  825. SetupFreeSourceListA(&ansilist,count);
  826. d = ERROR_INVALID_PARAMETER;
  827. b = FALSE;
  828. }
  829. }
  830. } else {
  831. d = ERROR_NOT_ENOUGH_MEMORY;
  832. b = FALSE;
  833. }
  834. SetupFreeSourceListW(&list,count);
  835. }
  836. SetLastError(d);
  837. return(b);
  838. }
  839. #else
  840. //
  841. // Unicode stub
  842. //
  843. BOOL
  844. SetupQuerySourceListW(
  845. IN DWORD Flags,
  846. OUT PCWSTR **List,
  847. OUT PUINT Count
  848. )
  849. {
  850. UNREFERENCED_PARAMETER(Flags);
  851. UNREFERENCED_PARAMETER(List);
  852. UNREFERENCED_PARAMETER(Count);
  853. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  854. return(FALSE);
  855. }
  856. #endif
  857. BOOL
  858. SetupQuerySourceList(
  859. IN DWORD Flags,
  860. OUT PCTSTR **List,
  861. OUT PUINT Count
  862. )
  863. /*++
  864. Routine Description:
  865. This routine allows the caller to query the current list of installation
  866. sources. The list is built from the system and user-specific lists,
  867. potentially overridden by a temporary list (see SetupSetSourceList).
  868. Arguments:
  869. Flags - a combination of the following values:
  870. SRCLIST_SYSTEM - specify that only the system list is desired.
  871. SRCLIST_USER - specify that only the per-user list is desired.
  872. SRCLIST_SYSIFADMIN - Same as SRCLIST_SYSTEM. Accepted only for
  873. compatibility.
  874. If none of these flags is specified then the current (merged) list is
  875. returned in its entirety.
  876. SRCLIST_NOSTRIPPLATFORM - Normally, all paths are stripped of a platform-
  877. specific component if that component is the final one. IE, a path
  878. stored in the registry as f:\mips will come back as f:\. If this flag
  879. is specified, this behavior is turned off.
  880. List - receives a pointer to an array of sources. The caller must free this
  881. with SetupFreeSourceList.
  882. Count - receives the number of sources.
  883. Return Value:
  884. --*/
  885. {
  886. DWORD d;
  887. PTSTR *Values1 = NULL;
  888. UINT NumVals1 = 0;
  889. PTSTR *Values2 = NULL;
  890. UINT NumVals2 = 0;
  891. UINT TotalVals;
  892. UINT u,v;
  893. BOOL Found;
  894. PTSTR *p;
  895. BOOL StripPlatform;
  896. //
  897. // Either caller wants sysifadmin, or he wants some combination of
  898. // system and user lists.
  899. //
  900. if((Flags & SRCLIST_SYSIFADMIN) && (Flags & (SRCLIST_SYSTEM | SRCLIST_USER))) {
  901. SetLastError(ERROR_INVALID_PARAMETER);
  902. return(FALSE);
  903. }
  904. if(!LockMruCritSect()) {
  905. return FALSE;
  906. }
  907. //
  908. // If sysifadmin, figure out which list to get.
  909. //
  910. if(Flags & SRCLIST_SYSIFADMIN) {
  911. //
  912. // Changed behavior to basically ignore this flag,
  913. // since setup doesn't record the system source in the per-user
  914. // mru list any more since this gets messy for upgrades.
  915. //
  916. //Flags = pSetupIsUserAdmin() ? SRCLIST_SYSTEM : SRCLIST_USER;
  917. Flags = SRCLIST_SYSTEM;
  918. } else {
  919. //
  920. // if no flags are specified, turn on system and user unless
  921. // there's a temporary list.
  922. //
  923. if(!Flags && !TemporarySourceList) {
  924. Flags = SRCLIST_SYSTEM | SRCLIST_USER;
  925. }
  926. }
  927. StripPlatform = ((Flags & SRCLIST_NOSTRIPPLATFORM) == 0);
  928. if(!Flags) {
  929. //
  930. // Temporary list in use.
  931. //
  932. d = NO_ERROR;
  933. if(Values1 = MyMalloc(TemporarySourceCount * sizeof(PTSTR))) {
  934. for(u=0; u<TemporarySourceCount; u++) {
  935. Values1[u] = DuplicateString(TemporarySourceList[u]);
  936. if(!Values1[u]) {
  937. d = ERROR_NOT_ENOUGH_MEMORY;
  938. for(v=0; v<u; v++) {
  939. MyFree(Values1[v]);
  940. }
  941. MyFree(Values1);
  942. break;
  943. }
  944. }
  945. if(d == NO_ERROR) {
  946. try {
  947. *List = Values1;
  948. *Count = TemporarySourceCount;
  949. } except(EXCEPTION_EXECUTE_HANDLER) {
  950. d = ERROR_INVALID_PARAMETER;
  951. }
  952. }
  953. } else {
  954. d = ERROR_NOT_ENOUGH_MEMORY;
  955. }
  956. } else {
  957. //
  958. // Fetch system list if desired.
  959. //
  960. if(Flags & SRCLIST_SYSTEM) {
  961. d = pSetupQueryMultiSzValueToArray(
  962. HKEY_LOCAL_MACHINE,
  963. pszPerSystemKey,
  964. pszPerSystemVal,
  965. &Values1,
  966. &NumVals1,
  967. FALSE
  968. );
  969. //
  970. // If we are supposed to, strip out platform-specific
  971. // trailing components.
  972. //
  973. if((d == NO_ERROR) && StripPlatform) {
  974. pSetupStripTrailingPlatformComponent(Values1,&NumVals1);
  975. } else if (d != NO_ERROR) {
  976. //
  977. // Create dummy array.
  978. //
  979. NumVals1 = 0;
  980. if(Values1 = MyMalloc(0)) {
  981. d = NO_ERROR;
  982. } else {
  983. d = ERROR_NOT_ENOUGH_MEMORY;
  984. }
  985. }
  986. } else {
  987. //
  988. // Create dummy array.
  989. //
  990. NumVals1 = 0;
  991. if(Values1 = MyMalloc(0)) {
  992. d = NO_ERROR;
  993. } else {
  994. d = ERROR_NOT_ENOUGH_MEMORY;
  995. }
  996. }
  997. //
  998. // Fetch user list if desired.
  999. //
  1000. if((d == NO_ERROR) && (Flags & SRCLIST_USER)) {
  1001. d = pSetupQueryMultiSzValueToArray(
  1002. HKEY_CURRENT_USER,
  1003. pszPerUserKey,
  1004. pszPerUserVal,
  1005. &Values2,
  1006. &NumVals2,
  1007. FALSE
  1008. );
  1009. if((d == NO_ERROR) && StripPlatform) {
  1010. pSetupStripTrailingPlatformComponent(Values2,&NumVals2);
  1011. }
  1012. } else if(Values1) {
  1013. //
  1014. // Create dummy array.
  1015. //
  1016. NumVals2 = 0;
  1017. if(Values2 = MyMalloc(0)) {
  1018. d = NO_ERROR;
  1019. } else {
  1020. d = ERROR_NOT_ENOUGH_MEMORY;
  1021. }
  1022. } else {
  1023. NumVals2 = 0;
  1024. Values2 = NULL;
  1025. d = ERROR_NOT_ENOUGH_MEMORY;
  1026. }
  1027. TotalVals = NumVals1;
  1028. if(d == NO_ERROR) {
  1029. //
  1030. // Merge lists. Favor the system list.
  1031. // We iterate through the user list. For each item in the user list,
  1032. // we look for it in the system list. If not found, we append to the system list.
  1033. // The system list becomes the final list.
  1034. //
  1035. for(u=0; (d == NO_ERROR) && (u<NumVals2); u++) {
  1036. //
  1037. // Look for the current per-user path in the per-system
  1038. // list. If not found, append to end of system list.
  1039. //
  1040. Found = FALSE;
  1041. for(v=0; v<NumVals1; v++) {
  1042. if(!lstrcmpi(Values1[v],Values2[u])) {
  1043. Found = TRUE;
  1044. break;
  1045. }
  1046. }
  1047. if(!Found) {
  1048. if(p = MyRealloc(Values1,(TotalVals+1)*sizeof(PTSTR))) {
  1049. Values1 = p;
  1050. if(Values1[TotalVals] = DuplicateString(Values2[u])) {
  1051. TotalVals++;
  1052. } else {
  1053. d = ERROR_NOT_ENOUGH_MEMORY;
  1054. }
  1055. } else {
  1056. d = ERROR_NOT_ENOUGH_MEMORY;
  1057. }
  1058. }
  1059. }
  1060. if(d == NO_ERROR) {
  1061. //
  1062. // Ensure that there's at least one item in the list.
  1063. //
  1064. if(TotalVals) {
  1065. try {
  1066. *List = Values1;
  1067. *Count = TotalVals;
  1068. Values1 = NULL; // no longer ours to free
  1069. } except(EXCEPTION_EXECUTE_HANDLER) {
  1070. d = ERROR_INVALID_PARAMETER;
  1071. }
  1072. } else {
  1073. try {
  1074. if(*List = MyMalloc(sizeof(PTSTR))) {
  1075. if(**List = DuplicateString(TEXT("A:\\"))) {
  1076. *Count = 1;
  1077. } else {
  1078. MyFree(*List);
  1079. d = ERROR_NOT_ENOUGH_MEMORY;
  1080. }
  1081. } else {
  1082. d = ERROR_NOT_ENOUGH_MEMORY;
  1083. }
  1084. } except(EXCEPTION_EXECUTE_HANDLER) {
  1085. //
  1086. // Note there is a tiny window for a memory leak here,
  1087. // if List pointer went bad between the MyMalloc
  1088. // and the DuplicateString. Oh well.
  1089. //
  1090. d = ERROR_INVALID_PARAMETER;
  1091. }
  1092. }
  1093. }
  1094. }
  1095. if (Values1) {
  1096. for(u=0; u<TotalVals; u++) {
  1097. if(Values1[u]) {
  1098. MyFree(Values1[u]);
  1099. }
  1100. }
  1101. MyFree(Values1);
  1102. }
  1103. if (Values2) {
  1104. for(u=0; u<NumVals2; u++) {
  1105. MyFree(Values2[u]);
  1106. }
  1107. MyFree(Values2);
  1108. }
  1109. }
  1110. LeaveCriticalSection(&MruCritSect);
  1111. SetLastError(d);
  1112. return(d == NO_ERROR);
  1113. }
  1114. BOOL
  1115. SetupFreeSourceListA(
  1116. IN OUT PCSTR **List,
  1117. IN UINT Count
  1118. )
  1119. {
  1120. //
  1121. // Not really ansi/unicode specific
  1122. //
  1123. return(SetupFreeSourceListW((PCWSTR **)List,Count));
  1124. }
  1125. BOOL
  1126. SetupFreeSourceListW(
  1127. IN OUT PCWSTR **List,
  1128. IN UINT Count
  1129. )
  1130. /*++
  1131. Routine Description:
  1132. This routine frees a source list as returned by SetupQuerySourceList.
  1133. Arguments:
  1134. Return Value:
  1135. --*/
  1136. {
  1137. UINT u;
  1138. BOOL b;
  1139. PCWSTR *list;
  1140. b = TRUE;
  1141. try {
  1142. list = *List;
  1143. for(u=0; u<Count; u++) {
  1144. if(list[u]) {
  1145. MyFree(list[u]);
  1146. }
  1147. }
  1148. MyFree(list);
  1149. *List = NULL;
  1150. } except(EXCEPTION_EXECUTE_HANDLER) {
  1151. b = FALSE;
  1152. }
  1153. return(b);
  1154. }
  1155. DWORD
  1156. pSetupGetList(
  1157. IN DWORD Flags,
  1158. OUT PCTSTR **List,
  1159. OUT PUINT Count,
  1160. OUT PBOOL NoBrowse
  1161. )
  1162. {
  1163. DWORD d;
  1164. if(!LockMruCritSect()) {
  1165. return ERROR_NOT_ENOUGH_MEMORY;
  1166. }
  1167. *NoBrowse = MruNoBrowse;
  1168. d = SetupQuerySourceList(Flags,List,Count) ? NO_ERROR : GetLastError();
  1169. LeaveCriticalSection(&MruCritSect);
  1170. return(d);
  1171. }
  1172. PTSTR
  1173. pSetupGetDefaultSourcePath(
  1174. IN HINF InfHandle,
  1175. IN DWORD Flags,
  1176. OUT PDWORD InfSourceMediaType
  1177. )
  1178. /*++
  1179. Routine Description:
  1180. This routine returns the default path string to be used for the
  1181. specified INF. It also returns the type of path, either a normal
  1182. file path or a URL.
  1183. The caller must free the string returned (if any) via MyFree.
  1184. Arguments:
  1185. InfHandle - Supplies a handle to the INF whose default source path
  1186. is to be retrieved.
  1187. Flags
  1188. - if SRCPATH_USEINFLOCATION bit is set, then return the directory
  1189. where the INF is located (with a source media type of SPOST_PATH)
  1190. in the case where either (a) the PNF has no source media information,
  1191. or (b) the PNF has SPOST_URL information.
  1192. - if SRCPATH_USEPNFINFORMATION bit is set, then the actual PNF
  1193. information (whether path or URL) is returned, and if the PNF
  1194. has no source media information, then the system source path is
  1195. returned.
  1196. InfSourceMediaType - Supplies the address of a variable that receives
  1197. the type of path returned. May be one of the following values:
  1198. SPOST_PATH - Standard file path
  1199. SPOST_URL - Internet path
  1200. Return Value:
  1201. If InfSourceMediaType is returned as SPOST_PATH, then a path will
  1202. always be returned, unless we're out of memory (or, if
  1203. DefaultPathIsInfLocation is TRUE, another possibility is that we hit an
  1204. exception). GetLastError() may be used in this case to indicate the cause of
  1205. failure).
  1206. If InfSourceMediaType is returned as SPOST_URL, then the return value
  1207. will be NULL if the default Code Download Manager URL is used (or if we ran
  1208. out of memory), otherwise it will be the specific URL to be used.
  1209. In either case, GetLastError() may be called to determine the cause of
  1210. failure (in the case of SPOST_URL for a NULL InfSourceMediaType,
  1211. GetLastError() will return NO_ERROR if we didn't fail (i.e., we meant to
  1212. return NULL because the INF came from the CDM website).
  1213. --*/
  1214. {
  1215. PTSTR InfSourcePath = NULL, p;
  1216. DWORD Err;
  1217. *InfSourceMediaType = SPOST_PATH;
  1218. Err = NO_ERROR;
  1219. //
  1220. // Lock the INF, so that we can get it's 'InfSourcePath' value, if present.
  1221. //
  1222. if(LockInf((PLOADED_INF)InfHandle)) {
  1223. try {
  1224. if(((PLOADED_INF)InfHandle)->InfSourcePath) {
  1225. InfSourcePath = DuplicateString(((PLOADED_INF)InfHandle)->InfSourcePath);
  1226. if(!InfSourcePath) {
  1227. Err = ERROR_NOT_ENOUGH_MEMORY;
  1228. goto clean0;
  1229. }
  1230. }
  1231. *InfSourceMediaType = ((PLOADED_INF)InfHandle)->InfSourceMediaType;
  1232. if(Flags & SRCPATH_USEINFLOCATION) {
  1233. //
  1234. // Caller has requested that we default to the INF's source
  1235. // location when there's no SPOST_PATH info.
  1236. //
  1237. if(*InfSourceMediaType != SPOST_PATH) {
  1238. if(InfSourcePath) {
  1239. MyFree(InfSourcePath);
  1240. InfSourcePath = NULL;
  1241. }
  1242. *InfSourceMediaType = SPOST_PATH;
  1243. }
  1244. if(!InfSourcePath) {
  1245. //
  1246. // Don't have an INF source path--use the INF's present
  1247. // location.
  1248. //
  1249. InfSourcePath = DuplicateString(((PLOADED_INF)InfHandle)->VersionBlock.Filename);
  1250. if(InfSourcePath) {
  1251. //
  1252. // OK, we duplicated the INF's full pathname, now
  1253. // truncate it to just the path part.
  1254. //
  1255. p = (PTSTR)pSetupGetFileTitle(InfSourcePath);
  1256. *p = TEXT('\0');
  1257. if(((p - InfSourcePath) != 3) ||
  1258. lstrcmp(CharNext(InfSourcePath), TEXT(":\\"))) {
  1259. //
  1260. // The path is not an "A:\" type path, so truncate
  1261. //
  1262. p = CharPrev(InfSourcePath, p);
  1263. MYASSERT(*p == TEXT('\\'));
  1264. if(p > InfSourcePath) {
  1265. *p = TEXT('\0');
  1266. }
  1267. }
  1268. } else {
  1269. Err = ERROR_NOT_ENOUGH_MEMORY;
  1270. goto clean0;
  1271. }
  1272. }
  1273. }
  1274. clean0: ; // nothing to do.
  1275. } except(EXCEPTION_EXECUTE_HANDLER) {
  1276. if(InfSourcePath) {
  1277. MyFree(InfSourcePath);
  1278. InfSourcePath = NULL;
  1279. }
  1280. Err = ERROR_INVALID_PARAMETER;
  1281. }
  1282. UnlockInf((PLOADED_INF)InfHandle);
  1283. }
  1284. if((Flags & SRCPATH_USEINFLOCATION) && !InfSourcePath) {
  1285. //
  1286. // We either hit out of memory or an exception--make sure media type
  1287. // specifies SPOST_PATH before returning failure.
  1288. //
  1289. *InfSourceMediaType = SPOST_PATH;
  1290. MYASSERT(Err != NO_ERROR);
  1291. SetLastError(Err);
  1292. return NULL;
  1293. }
  1294. if(!InfSourcePath && (*InfSourceMediaType == SPOST_PATH) && (Flags & SRCPATH_USEPNFINFORMATION)) {
  1295. //
  1296. // There's not an oem location associated with this INF, so use our default
  1297. // source path.
  1298. //
  1299. InfSourcePath = DuplicateString(SystemSourcePath);
  1300. if(!InfSourcePath) {
  1301. Err = ERROR_NOT_ENOUGH_MEMORY;
  1302. }
  1303. }
  1304. SetLastError(Err);
  1305. return InfSourcePath;
  1306. }
  1307. VOID
  1308. pSetupStripTrailingPlatformComponent(
  1309. IN OUT PTSTR *Paths,
  1310. IN OUT PDWORD NumPaths
  1311. )
  1312. {
  1313. PTSTR Path;
  1314. DWORD PathCount;
  1315. DWORD NewPathCount;
  1316. DWORD PathIndex;
  1317. DWORD DupIndex;
  1318. DWORD FirstIndex;
  1319. DWORD HoleCount;
  1320. PCTSTR Component;
  1321. UINT ComponentLength;
  1322. UINT PathLength;
  1323. int ComponentOffset;
  1324. UINT ComponentIndex;
  1325. //
  1326. // Do this for all paths in the array passed in by the caller.
  1327. //
  1328. PathCount = *NumPaths;
  1329. for(PathIndex=0; PathIndex<PathCount; PathIndex++) {
  1330. Path = Paths[PathIndex];
  1331. if(!Path) {
  1332. //
  1333. // skip holes
  1334. //
  1335. continue;
  1336. }
  1337. //
  1338. // See if the final path component matches one of the ones
  1339. // we care about.
  1340. //
  1341. PathLength = lstrlen(Path);
  1342. for(ComponentIndex=0; PlatformPathComponents[ComponentIndex]; ComponentIndex++) {
  1343. Component = PlatformPathComponents[ComponentIndex];
  1344. ComponentLength = lstrlen(Component);
  1345. ComponentOffset = PathLength - ComponentLength;
  1346. if((ComponentOffset > 0) && (lstrcmpi(Path+ComponentOffset,Component)==0)) {
  1347. //
  1348. // Got a match. Strip off the final component.
  1349. // Leave a trailing backslash if we're dealing with the root.
  1350. //
  1351. Path[ComponentOffset] = TEXT('\0');
  1352. if((Path[1] == TEXT(':')) && !Path[2]) {
  1353. Path[2] = TEXT('\\');
  1354. Path[3] = 0;
  1355. }
  1356. //
  1357. // Remove duplicate, preserving the first instance
  1358. //
  1359. for(FirstIndex=0 ; FirstIndex<PathIndex ; FirstIndex++) {
  1360. if(lstrcmpi(Paths[FirstIndex],Path) == 0) {
  1361. //
  1362. // we've found first instance
  1363. // and it's earlier than PathIndex
  1364. // so we'll end up deleting entry at PathIndex
  1365. Path = Paths[FirstIndex];
  1366. break;
  1367. }
  1368. }
  1369. for(DupIndex = FirstIndex+1;DupIndex<PathCount;DupIndex++) {
  1370. if(lstrcmpi(Paths[DupIndex],Path) == 0) {
  1371. //
  1372. // eliminate duplicate
  1373. //
  1374. MyFree(Paths[DupIndex]);
  1375. Paths[DupIndex] = NULL; // new hole - handle holes later
  1376. }
  1377. }
  1378. //
  1379. // only strip one component
  1380. //
  1381. break;
  1382. }
  1383. }
  1384. }
  1385. //
  1386. // now fix up 'holes' preserving order
  1387. //
  1388. HoleCount = 0;
  1389. for(PathIndex=0; PathIndex<PathCount; PathIndex++) {
  1390. if(!Paths[PathIndex]) {
  1391. //
  1392. // count holes
  1393. //
  1394. HoleCount++;
  1395. } else if(HoleCount) {
  1396. //
  1397. // shift down by number of holes found
  1398. //
  1399. Paths[PathIndex-HoleCount] = Paths[PathIndex];
  1400. }
  1401. }
  1402. NewPathCount = PathCount-HoleCount;
  1403. for(PathIndex = PathCount; PathIndex < NewPathCount; PathIndex++) {
  1404. Paths[PathIndex] = NULL;
  1405. }
  1406. *NumPaths = NewPathCount;
  1407. }