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.

803 lines
24 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1998 - 1998.
  5. //
  6. // File: special.hxx
  7. //
  8. // Contents: Special directories class and headers
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 23-Sep-99 PhilipLa Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #ifndef __SPECIAL_HXX__
  18. #define __SPECIAL_HXX__
  19. #include <filelist.hxx>
  20. #include <bothchar.hxx>
  21. const DWORD SPECIALDIR_MACHINE = 2;
  22. const DWORD SPECIALDIR_USER = 1;
  23. #define SDM SPECIALDIR_MACHINE
  24. #define SDU SPECIALDIR_USER
  25. struct sSpecialDir
  26. {
  27. TCHAR *ptsSpecialName;
  28. int iSpecialDir;
  29. DWORD dwFlags;
  30. TCHAR *ptsKeyName;
  31. };
  32. const sSpecialDir ssdSpecial[] = {
  33. {TEXT("%csidl_admintools%"), CSIDL_ADMINTOOLS,
  34. SDU, TEXT("Administrative Tools")},
  35. {TEXT("%csidl_altstartup%"), CSIDL_ALTSTARTUP,
  36. SDU, TEXT("AltStartup")},
  37. {TEXT("%csidl_appdata%"), CSIDL_APPDATA,
  38. SDU, TEXT("AppData")},
  39. {TEXT("%csidl_bitbucket%"), CSIDL_BITBUCKET,
  40. SDM, TEXT("RecycleBinFolder")},
  41. {TEXT("%csidl_common_admintools%"), CSIDL_COMMON_ADMINTOOLS,
  42. SDM, TEXT("Common Administrative Tools")},
  43. {TEXT("%csidl_common_altstartup%"), CSIDL_COMMON_ALTSTARTUP,
  44. SDU, TEXT("Common AltStartup")},
  45. {TEXT("%csidl_common_appdata%"), CSIDL_COMMON_APPDATA,
  46. SDM, TEXT("Common AppData")},
  47. {TEXT("%csidl_common_desktopdirectory%"), CSIDL_COMMON_DESKTOPDIRECTORY,
  48. SDM, TEXT("Common Desktop")},
  49. {TEXT("%csidl_common_documents%"), CSIDL_COMMON_DOCUMENTS,
  50. SDM, TEXT("Common Documents")},
  51. {TEXT("%csidl_common_favorites%"), CSIDL_COMMON_FAVORITES,
  52. SDM, TEXT("Common Favorites")},
  53. {TEXT("%csidl_common_programs%"), CSIDL_COMMON_PROGRAMS,
  54. SDM, TEXT("Common Programs")},
  55. {TEXT("%csidl_common_startmenu%"), CSIDL_COMMON_STARTMENU,
  56. SDM, TEXT("Common Start Menu")},
  57. {TEXT("%csidl_common_startup%"), CSIDL_COMMON_STARTUP,
  58. SDM, TEXT("Common Startup")},
  59. {TEXT("%csidl_common_templates%"), CSIDL_COMMON_TEMPLATES,
  60. SDM, TEXT("Common Templates")},
  61. {TEXT("%csidl_controls%"), CSIDL_CONTROLS,
  62. SDM, TEXT("ControlPanelFolder")},
  63. {TEXT("%csidl_cookies%"), CSIDL_COOKIES,
  64. SDU, TEXT("Cookies")},
  65. {TEXT("%csidl_desktop%"), CSIDL_DESKTOP,
  66. SDU, TEXT("Desktop")}, //The key shfolder.dll uses is "DesktopFolder"
  67. {TEXT("%csidl_desktopdirectory%"), CSIDL_DESKTOPDIRECTORY,
  68. SDU, TEXT("Desktop")},
  69. {TEXT("%csidl_drives%"), CSIDL_DRIVES,
  70. SDM, TEXT("DriveFolder")},
  71. {TEXT("%csidl_favorites%"), CSIDL_FAVORITES,
  72. SDU, TEXT("Favorites")},
  73. {TEXT("%csidl_fonts%"), CSIDL_FONTS,
  74. SDU, TEXT("Fonts")},
  75. {TEXT("%csidl_history%"), CSIDL_HISTORY,
  76. SDU, TEXT("History")},
  77. {TEXT("%csidl_internet%"), CSIDL_INTERNET,
  78. SDM, TEXT("InternetFolder")},
  79. {TEXT("%csidl_internet_cache%"), CSIDL_INTERNET_CACHE,
  80. SDU, TEXT("Cache")},
  81. {TEXT("%csidl_local_appdata%"), CSIDL_LOCAL_APPDATA,
  82. SDU, TEXT("Local AppData")},
  83. {TEXT("%csidl_mypictures%"), CSIDL_MYPICTURES,
  84. SDU, TEXT("My Pictures")},
  85. {TEXT("%csidl_nethood%"), CSIDL_NETHOOD,
  86. SDU, TEXT("Nethood")},
  87. {TEXT("%csidl_network%"), CSIDL_NETWORK,
  88. SDM, TEXT("NetworkFolder")},
  89. {TEXT("%csidl_personal%"), CSIDL_PERSONAL,
  90. SDU, TEXT("Personal")},
  91. {TEXT("%csidl_printers%"), CSIDL_PRINTERS,
  92. SDM, TEXT("PrintersFolder")},
  93. {TEXT("%csidl_printhood%"), CSIDL_PRINTHOOD,
  94. SDU, TEXT("PrintHood")},
  95. {TEXT("%csidl_profile%"), CSIDL_PROFILE,
  96. SDU, TEXT("Profile")},
  97. {TEXT("%csidl_program_files%"), CSIDL_PROGRAM_FILES,
  98. SDM, TEXT("ProgramFiles")},
  99. {TEXT("%csidl_program_files_common%"), CSIDL_PROGRAM_FILES_COMMON,
  100. SDM, TEXT("CommonProgramFiles")},
  101. {TEXT("%csidl_program_filesx86%"), CSIDL_PROGRAM_FILESX86,
  102. SDM, TEXT("ProgramFilesX86")},
  103. {TEXT("%csidl_programs%"), CSIDL_PROGRAMS,
  104. SDU, TEXT("Programs")},
  105. {TEXT("%csidl_recent%"), CSIDL_RECENT,
  106. SDU, TEXT("Recent")},
  107. {TEXT("%csidl_sendto%"), CSIDL_SENDTO,
  108. SDU, TEXT("SendTo")},
  109. {TEXT("%csidl_startmenu%"), CSIDL_STARTMENU,
  110. SDU, TEXT("Start Menu")},
  111. {TEXT("%csidl_startup%"), CSIDL_STARTUP,
  112. SDU, TEXT("Startup")},
  113. {TEXT("%csidl_system%"), CSIDL_SYSTEM,
  114. SDM, TEXT("System")},
  115. {TEXT("%csidl_systemx86%"), CSIDL_SYSTEMX86,
  116. SDM, TEXT("SystemX86")},
  117. {TEXT("%csidl_templates%"), CSIDL_TEMPLATES,
  118. SDU, TEXT("Templates")},
  119. {TEXT("%csidl_windows%"), CSIDL_WINDOWS,
  120. SDM, TEXT("Windows")}};
  121. const DWORD cSpecialDirs = sizeof(ssdSpecial) / sizeof(sSpecialDir);
  122. class CSpecialDirectory
  123. {
  124. public:
  125. CSpecialDirectory();
  126. ~CSpecialDirectory();
  127. inline const TCHAR * GetDirectoryName(ULONG i) const;
  128. inline const TCHAR * GetDirectoryPath(ULONG i) const;
  129. inline DWORD GetDirectoryCount(void) const;
  130. inline DWORD Init(void);
  131. inline DWORD InitForUser(HKEY hKey);
  132. inline DWORD InitFromInf(TCHAR *ptsName, TCHAR *ptsPath);
  133. inline DWORD AddSpecialDir(TCHAR *ptsName, TCHAR *ptsPath, LONG *i);
  134. inline DWORD ExpandMacro(TCHAR *pbuf,
  135. TCHAR *pbufTmp,
  136. TCHAR **pptsFinal,
  137. BOOL fUpdate);
  138. private:
  139. CFileList *_pfl;
  140. BOOL _fUser;
  141. };
  142. CSpecialDirectory::CSpecialDirectory()
  143. {
  144. _pfl = NULL;
  145. _fUser = FALSE;
  146. }
  147. CSpecialDirectory::~CSpecialDirectory()
  148. {
  149. FreeFileList(_pfl);
  150. }
  151. inline const TCHAR * CSpecialDirectory::GetDirectoryName(ULONG i) const
  152. {
  153. return _pfl->GetFullName(i);
  154. }
  155. inline const TCHAR * CSpecialDirectory::GetDirectoryPath(ULONG i) const
  156. {
  157. return _pfl->GetDestination(i);
  158. }
  159. inline DWORD CSpecialDirectory::GetDirectoryCount(void) const
  160. {
  161. return _pfl->GetNameCount();
  162. }
  163. inline DWORD CSpecialDirectory::AddSpecialDir(TCHAR *ptsName,
  164. TCHAR *ptsPath,
  165. LONG *piName)
  166. {
  167. DWORD dwErr = ERROR_SUCCESS;
  168. if (_pfl == NULL)
  169. {
  170. _pfl = new CFileList;
  171. if (_pfl == NULL)
  172. {
  173. Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
  174. return ERROR_OUTOFMEMORY;
  175. }
  176. }
  177. DWORD cDirs = _pfl->GetNameCount();
  178. for (ULONG i = 0; i < cDirs; i++)
  179. {
  180. if (_tcsicmp(_pfl->GetFullName(i), ptsName) == 0)
  181. {
  182. Win32PrintfResource(LogFile,
  183. IDS_FAILED);
  184. return ERROR_INVALID_PARAMETER;
  185. }
  186. }
  187. //Not found, so go ahead and try to add it.
  188. if (ptsPath == NULL)
  189. {
  190. //Look it up
  191. HRESULT hr;
  192. int iFolder = 0;
  193. //It's a new one, see if it matches anything in the special
  194. //dir list
  195. for (i = 0; i < cSpecialDirs; i++)
  196. {
  197. if (_tcsicmp(ptsName, ssdSpecial[i].ptsSpecialName) == 0)
  198. {
  199. //Bingo
  200. iFolder = ssdSpecial[i].iSpecialDir;
  201. break;
  202. }
  203. }
  204. if (iFolder != 0)
  205. {
  206. TCHAR tsDirName[MAX_PATH + 1];
  207. hr = SHGetFolderPath(NULL,
  208. iFolder,
  209. NULL,
  210. SHGFP_TYPE_CURRENT,
  211. tsDirName);
  212. //Only handle success cases, since some special paths aren't
  213. //available on all machines.
  214. if (SUCCEEDED(hr) && (tsDirName[0] != 0))
  215. {
  216. dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
  217. if (dwErr)
  218. return dwErr;
  219. dwErr = _pfl->SetDestination(tsDirName, (ULONG)*piName);
  220. return dwErr;
  221. }
  222. }
  223. //It isn't a special directory, see if it's an environment
  224. //variable.
  225. TCHAR tsVarName[MAX_PATH + 1];
  226. TCHAR tsVarValue[MAX_PATH + 1];
  227. if (_tcslen(ptsName) > MAX_PATH)
  228. {
  229. if (DebugOutput)
  230. Win32Printf(LogFile, "Error: ptsName too long %s\r\n", ptsName);
  231. return ERROR_FILENAME_EXCED_RANGE;
  232. }
  233. _tcscpy(tsVarName, ptsName + 1);
  234. tsVarName[_tcslen(tsVarName) - 1] = 0;
  235. dwErr = ERROR_INVALID_PARAMETER;
  236. if (_fUser)
  237. {
  238. //Check for userprofile, which is a special case
  239. if (_tcsicmp(tsVarName, TEXT("USERPROFILE")) == 0)
  240. {
  241. //We have the user's profile directory stored
  242. //in UserPath
  243. if (_tcslen(UserPath) > MAX_PATH)
  244. {
  245. if (DebugOutput)
  246. Win32Printf(LogFile, "Error: UserPath too long %s\r\n", UserPath);
  247. return ERROR_FILENAME_EXCED_RANGE;
  248. }
  249. _tcscpy(tsVarValue, UserPath);
  250. dwErr = ERROR_SUCCESS;
  251. }
  252. else if (CurrentUser != NULL)
  253. {
  254. //Check in the registry
  255. HKEY hKeyEnv = NULL;
  256. dwErr = RegOpenKey(CurrentUser,
  257. TEXT("Environment"),
  258. &hKeyEnv);
  259. if (dwErr)
  260. {
  261. //Do nothing, try again below
  262. }
  263. else
  264. {
  265. DWORD dwValueType;
  266. DWORD dwLen = MAX_PATH + 1;
  267. dwErr = RegQueryValueEx(hKeyEnv,
  268. tsVarName,
  269. NULL,
  270. &dwValueType,
  271. (BYTE *)tsVarValue,
  272. &dwLen);
  273. if ((dwErr == ERROR_SUCCESS) &&
  274. (dwValueType != REG_SZ))
  275. {
  276. //Flag the error, try again below
  277. dwErr = ERROR_INVALID_PARAMETER;
  278. }
  279. RegCloseKey(hKeyEnv);
  280. }
  281. }
  282. }
  283. if (dwErr != ERROR_SUCCESS)
  284. {
  285. dwErr = GetEnvironmentVariable(tsVarName,
  286. tsVarValue,
  287. MAX_PATH + 1);
  288. //GetEnvironmentVariable returns a length, not an error code.
  289. //A value of 0 is an error here.
  290. if (dwErr == 0)
  291. {
  292. // if no mapping found, it might not actually be a macro.
  293. // just continue without setting this as a special dir
  294. if (Verbose)
  295. {
  296. Win32Printf(LogFile,
  297. "Warning: Couldn't find macro mapping for [%s]. "
  298. "Assuming not a macro\r\n",
  299. ptsName);
  300. }
  301. *piName = -1;
  302. return ERROR_SUCCESS;
  303. }
  304. }
  305. dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
  306. if (dwErr)
  307. return dwErr;
  308. dwErr = _pfl->SetDestination(tsVarValue, (ULONG)*piName);
  309. }
  310. else
  311. {
  312. dwErr = _pfl->SetName(ptsName, (ULONG *)piName, FALSE);
  313. if (dwErr)
  314. return dwErr;
  315. dwErr = _pfl->SetDestination(ptsPath, (ULONG)*piName);
  316. }
  317. return dwErr;
  318. }
  319. inline DWORD CSpecialDirectory::InitFromInf(TCHAR *ptsName, TCHAR *ptsPath)
  320. {
  321. LONG i;
  322. return AddSpecialDir(ptsName, ptsPath, &i);
  323. }
  324. inline DWORD CSpecialDirectory::Init(void)
  325. {
  326. _pfl = new CFileList;
  327. if (_pfl == NULL)
  328. {
  329. Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
  330. return ERROR_OUTOFMEMORY;
  331. }
  332. for (ULONG i = 0; i < cSpecialDirs; i++)
  333. {
  334. HRESULT hr;
  335. ULONG iName;
  336. TCHAR tsDirName[MAX_PATH + 1];
  337. DWORD dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName,
  338. &iName,
  339. FALSE);
  340. if (dwErr)
  341. return dwErr;
  342. hr = SHGetFolderPath(NULL,
  343. ssdSpecial[i].iSpecialDir,
  344. NULL,
  345. SHGFP_TYPE_CURRENT,
  346. tsDirName);
  347. //Only handle success cases, since some special paths aren't
  348. //available on all machines.
  349. if (SUCCEEDED(hr))
  350. {
  351. if (tsDirName[0] == 0)
  352. {
  353. //WTF happened? Why didn't we get an error?
  354. //Continue anyway, we can ignore this case
  355. }
  356. else
  357. {
  358. dwErr = _pfl->SetDestination(tsDirName, iName);
  359. if (dwErr)
  360. return dwErr;
  361. }
  362. }
  363. }
  364. return ERROR_SUCCESS;
  365. }
  366. inline DWORD CSpecialDirectory::InitForUser(HKEY hKey)
  367. {
  368. DWORD dwErr = ERROR_SUCCESS;
  369. TCHAR *ptsValData = NULL;
  370. _fUser = TRUE;
  371. _pfl = new CFileList;
  372. if (_pfl == NULL)
  373. {
  374. Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
  375. return ERROR_OUTOFMEMORY;
  376. }
  377. HKEY hKeyUser = NULL;
  378. HKEY hKeyEnv = NULL;
  379. //Pick up all the environment strings from the user's registry block
  380. dwErr = RegOpenKey(CurrentUser,
  381. TEXT("Environment"),
  382. &hKeyEnv);
  383. if (dwErr)
  384. {
  385. Win32PrintfResource(LogFile,
  386. ResultToRC(dwErr));
  387. goto Cleanup;
  388. }
  389. DWORD dwIndex;
  390. dwIndex = 0;
  391. do
  392. {
  393. TCHAR tsValName[MAX_PATH + 1];
  394. DWORD dwValNameLength = (MAX_PATH + 1);
  395. DWORD dwValDataLength = 0;
  396. DWORD dwType;
  397. // Read the value name
  398. dwErr = RegEnumValue(hKeyEnv,
  399. dwIndex,
  400. tsValName,
  401. &dwValNameLength,
  402. NULL, //reserved
  403. NULL, // dont care about type
  404. NULL, // dont care about data
  405. NULL); // dont care about datalength
  406. if (dwErr == ERROR_NO_MORE_ITEMS)
  407. {
  408. dwErr = ERROR_SUCCESS;
  409. break;
  410. }
  411. if (dwErr)
  412. {
  413. Win32PrintfResource(LogFile,
  414. ResultToRC(dwErr));
  415. goto Cleanup;
  416. }
  417. // Read the size required for the data
  418. dwErr = RegQueryValueEx(hKeyEnv,
  419. tsValName,
  420. NULL, // reserved
  421. NULL, // dont care about type
  422. NULL, // dont care about data
  423. &dwValDataLength);
  424. if (dwErr)
  425. {
  426. Win32PrintfResource(LogFile,
  427. ResultToRC(dwErr));
  428. goto Cleanup;
  429. }
  430. ptsValData = (TCHAR *)malloc(dwValDataLength + 1);
  431. if (NULL == ptsValData)
  432. {
  433. Win32PrintfResource(LogFile, IDS_NOT_ENOUGH_MEMORY);
  434. return ERROR_OUTOFMEMORY;
  435. }
  436. // Read the actual data
  437. dwErr = RegQueryValueEx(hKeyEnv,
  438. tsValName,
  439. NULL, // reserved
  440. &dwType,
  441. (BYTE *)ptsValData,
  442. &dwValDataLength);
  443. if (dwErr)
  444. {
  445. Win32PrintfResource(LogFile,
  446. ResultToRC(dwErr));
  447. goto Cleanup;
  448. }
  449. if ((dwType == REG_SZ) ||
  450. (dwType == REG_EXPAND_SZ))
  451. {
  452. DWORD i;
  453. //Add %s
  454. TCHAR tsFinal[MAX_PATH+1];
  455. if (dwValNameLength > MAX_PATH-1)
  456. {
  457. dwErr = ERROR_FILENAME_EXCED_RANGE;
  458. goto Cleanup;
  459. }
  460. tsFinal[0] = TEXT('%');
  461. _tcscpy(tsFinal+1, tsValName);
  462. tsFinal[dwValNameLength + 1] = TEXT('%');
  463. tsFinal[dwValNameLength + 2] = 0;
  464. dwErr = _pfl->SetName(tsFinal, &i, FALSE);
  465. if (dwErr)
  466. goto Cleanup;
  467. dwErr = _pfl->SetDestination(ptsValData, i);
  468. if (dwErr)
  469. goto Cleanup;
  470. }
  471. dwIndex++;
  472. free (ptsValData); ptsValData = NULL;
  473. } while (1);
  474. //Also pick up USERPROFILE so we don't need to get it later.
  475. dwErr = _pfl->SetName(TEXT("%USERPROFILE%"), &dwIndex, FALSE);
  476. if (dwErr)
  477. goto Cleanup;
  478. dwErr = _pfl->SetDestination(UserPath, dwIndex);
  479. if (dwErr)
  480. goto Cleanup;
  481. dwErr = RegOpenKey(hKey,
  482. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"),
  483. &hKeyUser);
  484. if (dwErr)
  485. {
  486. Win32PrintfResource(LogFile,
  487. ResultToRC(dwErr));
  488. goto Cleanup;
  489. }
  490. ULONG i;
  491. for (i = 0; i < cSpecialDirs; i++)
  492. {
  493. HRESULT hr;
  494. ULONG iName;
  495. TCHAR tsDirName[MAX_PATH + 1];
  496. int iSpecialDir = ssdSpecial[i].iSpecialDir;
  497. dwErr = _pfl->SetName(ssdSpecial[i].ptsSpecialName,
  498. &iName,
  499. FALSE);
  500. if (dwErr)
  501. {
  502. goto Cleanup;
  503. }
  504. if (ssdSpecial[i].dwFlags & SDU)
  505. {
  506. //User specific directory, get from appropriate place in
  507. //hKey
  508. TCHAR *ptsFinal;
  509. TCHAR tsMacro[MAX_PATH + 1]; // ptsFinal points here after ExpandMacro call
  510. if (iSpecialDir == CSIDL_DESKTOP)
  511. {
  512. //Another special case. This is the user profile directory
  513. //plus a localized desktop directory name, but we don't
  514. //have that localization info, so we're in trouble.
  515. //
  516. //Pretend we're doing CSIDL_DESKTOPDIRECTORY instead
  517. iSpecialDir = CSIDL_DESKTOPDIRECTORY;
  518. }
  519. if (iSpecialDir == CSIDL_PROFILE)
  520. {
  521. //Special case, not stored in registry
  522. if (_tcslen(UserPath) > MAX_PATH)
  523. {
  524. if (DebugOutput)
  525. Win32Printf(LogFile, "Error: UserPath Too Long %s\r\n", UserPath);
  526. dwErr = ERROR_FILENAME_EXCED_RANGE;
  527. goto Cleanup;
  528. }
  529. _tcscpy(tsDirName, UserPath);
  530. dwErr = ERROR_SUCCESS;
  531. ptsFinal = tsDirName;
  532. }
  533. else
  534. {
  535. DWORD dwValueType;
  536. DWORD dwLen = MAX_PATH + 1;
  537. dwErr = RegQueryValueEx(hKeyUser,
  538. ssdSpecial[i].ptsKeyName,
  539. NULL,
  540. &dwValueType,
  541. (BYTE *)tsDirName,
  542. &dwLen);
  543. if (dwErr == ERROR_SUCCESS)
  544. {
  545. if (dwValueType == REG_EXPAND_SZ)
  546. {
  547. dwErr = ExpandMacro(tsDirName, tsMacro, &ptsFinal, TRUE);
  548. if (dwErr)
  549. goto Cleanup;
  550. }
  551. else
  552. {
  553. ptsFinal = tsDirName;
  554. }
  555. }
  556. }
  557. if (dwErr == ERROR_SUCCESS)
  558. {
  559. dwErr = _pfl->SetDestination(ptsFinal, iName);
  560. if (dwErr)
  561. goto Cleanup;
  562. continue;
  563. }
  564. }
  565. dwErr = ERROR_SUCCESS;
  566. //Machine global directory, get through normal mechanism
  567. hr = SHGetFolderPath(NULL,
  568. iSpecialDir,
  569. NULL,
  570. SHGFP_TYPE_CURRENT,
  571. tsDirName);
  572. //Only handle success cases, since some special paths aren't
  573. //available on all machines.
  574. if (SUCCEEDED(hr))
  575. {
  576. if (tsDirName[0] == 0)
  577. {
  578. //WTF happened? Why didn't we get an error?
  579. //Continue anyway, we can ignore this case
  580. }
  581. else
  582. {
  583. dwErr = _pfl->SetDestination(tsDirName, iName);
  584. if (dwErr)
  585. {
  586. goto Cleanup;
  587. }
  588. }
  589. }
  590. }
  591. Cleanup:
  592. if (hKeyUser != NULL)
  593. RegCloseKey(hKeyUser);
  594. if (hKeyEnv != NULL)
  595. RegCloseKey(hKeyEnv);
  596. if (ptsValData != NULL)
  597. free(ptsValData);
  598. return dwErr;
  599. }
  600. // Notes (chrisab 3/1/00): this function expands nested macros, but
  601. // does not expand multiple macros in the same source string. Could
  602. // also be changed to use cleaner temp buffers.
  603. DWORD CSpecialDirectory::ExpandMacro(TCHAR *pbuf,
  604. TCHAR *pbufTmp,
  605. TCHAR **pptsFinal,
  606. BOOL fUpdate)
  607. {
  608. TCHAR *ptsFinalName = pbuf;
  609. TCHAR *ptsMacro;
  610. // Point the final at the original, in case we
  611. // return early without making any substitutions
  612. *pptsFinal = pbuf;
  613. while ((ptsMacro = _tcschr(ptsFinalName, TEXT('%'))) != NULL)
  614. {
  615. //% found, find the closing %
  616. TCHAR tsMacro[MAX_PATH + 1];
  617. TCHAR *ptsMacroEnd;
  618. ptsMacroEnd = _tcschr(ptsMacro + 1, TEXT('%'));
  619. if (ptsMacroEnd == NULL)
  620. {
  621. // '%' is a valid character in files, too.
  622. // Assume if no match, it's not an error
  623. return ERROR_SUCCESS;
  624. }
  625. if (ptsMacroEnd - ptsMacro + 1 > MAX_PATH)
  626. {
  627. if (DebugOutput)
  628. Win32Printf(LogFile,
  629. "Error: ptsMacro too long %*s\r\n",
  630. ptsMacroEnd - ptsMacro + 1,
  631. ptsMacro);
  632. return ERROR_FILENAME_EXCED_RANGE;
  633. }
  634. _tcsncpy(tsMacro, ptsMacro, ptsMacroEnd - ptsMacro + 1);
  635. tsMacro[ptsMacroEnd - ptsMacro + 1] = 0;
  636. if (DebugOutput)
  637. {
  638. Win32Printf(LogFile, "Found macro %s\r\n", tsMacro);
  639. }
  640. const TCHAR *ptsDirMapping = NULL;
  641. for (ULONG i = 0; i < GetDirectoryCount(); i++)
  642. {
  643. if (_tcsicmp(tsMacro, GetDirectoryName(i)) == 0)
  644. {
  645. //Winnah
  646. ptsDirMapping = GetDirectoryPath(i);
  647. // Set fUpdate to FALSE so we don't try to add this again
  648. // for when the Name is a macro we can't expand
  649. fUpdate = FALSE;
  650. break;
  651. }
  652. }
  653. if ((ptsDirMapping == NULL) && fUpdate)
  654. {
  655. //It's a new one, try to add it.
  656. LONG iName;
  657. DWORD dwErr = AddSpecialDir(tsMacro, NULL, &iName);
  658. if (dwErr)
  659. return dwErr;
  660. if (iName >= 0)
  661. {
  662. ptsDirMapping = GetDirectoryPath(iName);
  663. }
  664. }
  665. if (ptsDirMapping == NULL)
  666. {
  667. // If no mapping found, it might not actually be a macro.
  668. // Just leave it as it is
  669. return ERROR_SUCCESS;
  670. }
  671. if (DebugOutput)
  672. Win32Printf(LogFile,
  673. "Mapping for %s == %s\r\n",
  674. tsMacro,
  675. ptsDirMapping);
  676. //Do substitution
  677. TCHAR *ptsWorkBuffer = (ptsFinalName == pbuf) ? pbufTmp : pbuf;
  678. if ((ptsMacro - ptsFinalName) + _tcslen(ptsDirMapping) + _tcslen(ptsMacroEnd+1) > MAX_PATH)
  679. {
  680. if (Verbose)
  681. {
  682. Win32Printf(LogFile, "Error too long filename: %*s\\%s\\%s\r\n",
  683. ptsMacro-ptsFinalName, // int: length of ptsFinalName to print
  684. ptsFinalName, ptsDirMapping, ptsMacroEnd+1);
  685. }
  686. return ERROR_FILENAME_EXCED_RANGE;
  687. }
  688. _tcsncpy(ptsWorkBuffer, ptsFinalName, ptsMacro - ptsFinalName);
  689. _tcscpy(ptsWorkBuffer + (ptsMacro - ptsFinalName),
  690. ptsDirMapping);
  691. _tcscpy(ptsWorkBuffer +
  692. (ptsMacro - ptsFinalName) +
  693. _tcslen(ptsDirMapping),
  694. ptsMacroEnd + 1);
  695. if (DebugOutput)
  696. Win32Printf(LogFile, "New string: %s\r\n", ptsWorkBuffer);
  697. ptsFinalName = ptsWorkBuffer;
  698. }
  699. *pptsFinal = ptsFinalName;
  700. return ERROR_SUCCESS;
  701. }
  702. #endif // #ifndef __SPECIAL_HXX__