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.

794 lines
21 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1997.
  5. //
  6. // File: path.cxx
  7. //
  8. // Contents: Common routines for processing file and pathnames.
  9. //
  10. // History: 11-22-1996 DavidMun Created
  11. //
  12. //---------------------------------------------------------------------------
  13. #include "..\pch\headers.hxx"
  14. #pragma hdrstop
  15. #include <regstr.h> // for app path reg key constant
  16. #include "..\inc\common.hxx"
  17. #include "..\inc\misc.hxx"
  18. #include "..\inc\debug.hxx"
  19. //
  20. // Forward references
  21. //
  22. LPCTSTR
  23. FindFirstTrailingSpace(LPCTSTR ptsz);
  24. BOOL
  25. FileExistsInPath(
  26. LPTSTR ptszFilename,
  27. LPCTSTR ptszPath,
  28. LPTSTR ptszFoundFile,
  29. ULONG cchFoundBuf);
  30. //+--------------------------------------------------------------------------
  31. //
  32. // Function: ProcessApplicationName
  33. //
  34. // Synopsis: Canonicalize and search for [ptszFilename].
  35. //
  36. // Arguments: [ptszFilename] - must be at least MAX_PATH chars long
  37. // [tszWorkingDir] - "" or a directory not ending in slash
  38. //
  39. // Returns: TRUE - a filename was found
  40. // FALSE - no filename found
  41. //
  42. // Modifies: *[ptszFilename]
  43. //
  44. // History: 11-21-1996 DavidMun Created
  45. // 06-03-1997 DavidMun Expand environment vars
  46. //
  47. // Notes: This function should only be called to process filenames
  48. // on the local machine.
  49. //
  50. //---------------------------------------------------------------------------
  51. BOOL
  52. ProcessApplicationName(LPTSTR ptszFilename, LPCTSTR tszWorkingDir)
  53. {
  54. BOOL fFound = FALSE;
  55. TCHAR tszFilenameWithExe[MAX_PATH];
  56. //
  57. // Use tszFilenameWithExe as a temporary buffer for preparing the string
  58. // in ptszFilename. Get rid of lead/trail spaces and double quotes, then
  59. // expand environment strings.
  60. //
  61. lstrcpy(tszFilenameWithExe, ptszFilename);
  62. StripLeadTrailSpace(tszFilenameWithExe);
  63. DeleteQuotes(tszFilenameWithExe);
  64. ExpandEnvironmentStrings(tszFilenameWithExe, ptszFilename, MAX_PATH);
  65. tszFilenameWithExe[0] = TEXT('\0');
  66. ULONG cchFilename = lstrlen(ptszFilename);
  67. //
  68. // If the filename doesn't end with .exe, and the resulting string
  69. // wouldn't be greater than MAX_PATH, create a version of the filename
  70. // with .exe appended.
  71. //
  72. // Note this will prevent us from finding foo.exe.exe when we're given
  73. // foo.exe, but the performance gained by excluding this case outweighs
  74. // the value of completeness, since it's unlikely anyone would create
  75. // such a filename.
  76. //
  77. if (cchFilename < MAX_PATH - 4)
  78. {
  79. LPTSTR ptszLastDot = _tcsrchr(ptszFilename, TEXT('.'));
  80. if (!ptszLastDot || lstrcmpi(ptszLastDot, DOTEXE))
  81. {
  82. lstrcpy(tszFilenameWithExe, ptszFilename);
  83. lstrcpy(&tszFilenameWithExe[cchFilename], DOTEXE);
  84. }
  85. }
  86. do
  87. {
  88. //
  89. // If the user specified path information (if there is a colon or
  90. // backslash anywhere in the string), look for the file as
  91. // specified or with .exe appended, but look nowhere else.
  92. //
  93. if (_tcspbrk(ptszFilename, TEXT(":\\")))
  94. {
  95. if (*tszFilenameWithExe)
  96. {
  97. fFound = FileExists(tszFilenameWithExe);
  98. if (fFound)
  99. {
  100. lstrcpy(ptszFilename, tszFilenameWithExe);
  101. }
  102. }
  103. if (!fFound)
  104. {
  105. fFound = FileExists(ptszFilename);
  106. }
  107. break;
  108. }
  109. //
  110. // First try the working directory
  111. //
  112. TCHAR tszFoundFile[MAX_PATH] = TEXT("");
  113. if (*tszWorkingDir)
  114. {
  115. if (*tszFilenameWithExe)
  116. {
  117. fFound = FileExistsInPath(tszFilenameWithExe,
  118. tszWorkingDir,
  119. tszFoundFile,
  120. MAX_PATH);
  121. }
  122. if (!fFound)
  123. {
  124. fFound = FileExistsInPath(ptszFilename,
  125. tszWorkingDir,
  126. tszFoundFile,
  127. MAX_PATH);
  128. }
  129. if (fFound)
  130. {
  131. lstrcpy(ptszFilename, tszFoundFile);
  132. break;
  133. }
  134. }
  135. //
  136. // Next try using the app paths key
  137. //
  138. TCHAR tszAppPathVar[MAX_PATH_VALUE] = TEXT("");
  139. TCHAR tszAppPathDefault[MAX_PATH] = TEXT("");
  140. if (*tszFilenameWithExe)
  141. {
  142. GetAppPathInfo(tszFilenameWithExe,
  143. tszAppPathDefault,
  144. MAX_PATH,
  145. tszAppPathVar,
  146. MAX_PATH_VALUE);
  147. }
  148. if (!*tszAppPathDefault && !*tszAppPathVar)
  149. {
  150. GetAppPathInfo(ptszFilename,
  151. tszAppPathDefault,
  152. MAX_PATH,
  153. tszAppPathVar,
  154. MAX_PATH_VALUE);
  155. }
  156. //
  157. // If there was a default value, try that
  158. //
  159. if (*tszAppPathDefault)
  160. {
  161. fFound = FileExists(tszAppPathDefault);
  162. if (fFound)
  163. {
  164. lstrcpy(ptszFilename, tszAppPathDefault);
  165. break;
  166. }
  167. //
  168. // If there's room, concat .exe to the default and look for
  169. // that
  170. //
  171. ULONG cchDefault = lstrlen(tszAppPathDefault);
  172. if (cchDefault < MAX_PATH - 4)
  173. {
  174. lstrcat(tszAppPathDefault, DOTEXE);
  175. fFound = FileExists(tszAppPathDefault);
  176. if (fFound)
  177. {
  178. lstrcpy(ptszFilename, tszAppPathDefault);
  179. break;
  180. }
  181. }
  182. }
  183. //
  184. // If the app path key specified a value for the PATH variable,
  185. // try looking in all the directories it specifies
  186. //
  187. if (*tszAppPathVar)
  188. {
  189. if (*tszFilenameWithExe)
  190. {
  191. fFound = FileExistsInPath(tszFilenameWithExe,
  192. tszAppPathVar,
  193. tszFoundFile,
  194. MAX_PATH);
  195. }
  196. if (!fFound)
  197. {
  198. fFound = FileExistsInPath(ptszFilename,
  199. tszAppPathVar,
  200. tszFoundFile,
  201. MAX_PATH);
  202. }
  203. if (fFound)
  204. {
  205. lstrcpy(ptszFilename, tszFoundFile);
  206. break;
  207. }
  208. }
  209. //
  210. // Try looking along the system PATH variable
  211. //
  212. ULONG cchPath;
  213. TCHAR tszSystemPath[MAX_PATH_VALUE] = TEXT("");
  214. cchPath = GetEnvironmentVariable(TEXT("Path"),
  215. tszSystemPath,
  216. MAX_PATH_VALUE);
  217. if (!cchPath || cchPath > MAX_PATH_VALUE)
  218. {
  219. break;
  220. }
  221. if (*tszFilenameWithExe)
  222. {
  223. fFound = FileExistsInPath(tszFilenameWithExe,
  224. tszSystemPath,
  225. tszFoundFile,
  226. MAX_PATH);
  227. }
  228. if (!fFound)
  229. {
  230. fFound = FileExistsInPath(ptszFilename,
  231. tszSystemPath,
  232. tszFoundFile,
  233. MAX_PATH);
  234. }
  235. if (fFound)
  236. {
  237. lstrcpy(ptszFilename, tszFoundFile);
  238. }
  239. } while (0);
  240. return fFound;
  241. }
  242. //+--------------------------------------------------------------------------
  243. //
  244. // Function: IsLocalFilename
  245. //
  246. // Synopsis: Return TRUE if [tszFilename] represents a file on the local
  247. // machine, FALSE otherwise.
  248. //
  249. // History: 1-31-1997 DavidMun Created
  250. //
  251. //---------------------------------------------------------------------------
  252. BOOL
  253. IsLocalFilename(LPCTSTR tszFilename)
  254. {
  255. if (!tszFilename || !*tszFilename)
  256. {
  257. return FALSE;
  258. }
  259. if (tszFilename[0] == TEXT('\\') && tszFilename[1] == TEXT('\\'))
  260. {
  261. //
  262. // Find the length of the portion of the name belonging to the machine name
  263. //
  264. LPCTSTR ptszNextSlash = _tcschr(tszFilename + 2, TEXT('\\'));
  265. if (!ptszNextSlash)
  266. {
  267. return FALSE;
  268. }
  269. DWORD cchMachineName = (DWORD)(ptszNextSlash - tszFilename - 2);
  270. //
  271. // Get the local machine name (both NetBIOS and FQDN) to compare with that passed in.
  272. //
  273. TCHAR tszLocalName[SA_MAX_COMPUTERNAME_LENGTH + 1];
  274. DWORD cchLocalName = SA_MAX_COMPUTERNAME_LENGTH + 1;
  275. if (!GetComputerName(tszLocalName, &cchLocalName))
  276. {
  277. ERR_OUT("IsLocalFilename: GetComputerName", HRESULT_FROM_WIN32(GetLastError()));
  278. return FALSE;
  279. }
  280. TCHAR tszFQDN[SA_MAX_COMPUTERNAME_LENGTH + 1];
  281. DWORD cchFQDN = SA_MAX_COMPUTERNAME_LENGTH + 1;
  282. if (!GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified, tszFQDN, &cchFQDN))
  283. {
  284. ERR_OUT("IsLocalFilename: GetComputerNameEx", HRESULT_FROM_WIN32(GetLastError()));
  285. return FALSE;
  286. }
  287. //
  288. // Return whether we have a match on the machine name portion.
  289. // I'm assuming that we won't have a case where the NetBIOS name
  290. // and the FQDN are the same length but different.
  291. //
  292. if (cchMachineName == cchLocalName)
  293. {
  294. return lstrcmpi(tszFilename + 2, tszLocalName) == 0;
  295. }
  296. else if (cchMachineName == cchFQDN)
  297. {
  298. return lstrcmpi(tszFilename + 2, tszFQDN) == 0;
  299. }
  300. else
  301. {
  302. // if the lengths didn't match, there's no need
  303. // to even bother with a string comparison
  304. return FALSE;
  305. }
  306. }
  307. if (s_isDriveLetter(tszFilename[0]) && tszFilename[1] == TEXT(':'))
  308. {
  309. TCHAR tszRoot[] = TEXT("x:\\");
  310. tszRoot[0] = tszFilename[0];
  311. UINT uiType = GetDriveType(tszRoot);
  312. if (uiType == DRIVE_REMOTE || uiType == 0 || uiType == 1)
  313. {
  314. return FALSE;
  315. }
  316. }
  317. return TRUE;
  318. }
  319. //+--------------------------------------------------------------------------
  320. //
  321. // Function: StripLeadTrailSpace
  322. //
  323. // Synopsis: Delete leading and trailing spaces from [ptsz].
  324. //
  325. // History: 11-22-1996 DavidMun Created
  326. //
  327. //---------------------------------------------------------------------------
  328. VOID
  329. StripLeadTrailSpace(LPTSTR ptsz)
  330. {
  331. ULONG cchLeadingSpace = _tcsspn(ptsz, TEXT(" \t"));
  332. ULONG cch = lstrlen(ptsz);
  333. //
  334. // If there are any leading spaces or tabs, move the string
  335. // (including its nul terminator) left to delete them.
  336. //
  337. if (cchLeadingSpace)
  338. {
  339. MoveMemory(ptsz,
  340. ptsz + cchLeadingSpace,
  341. (cch - cchLeadingSpace + 1) * sizeof(TCHAR));
  342. cch -= cchLeadingSpace;
  343. }
  344. //
  345. // Concatenate at the first trailing space
  346. //
  347. LPTSTR ptszTrailingSpace = (LPTSTR) FindFirstTrailingSpace(ptsz);
  348. if (ptszTrailingSpace)
  349. {
  350. *ptszTrailingSpace = TEXT('\0');
  351. }
  352. }
  353. //+--------------------------------------------------------------------------
  354. //
  355. // Function: FindFirstTrailingSpace
  356. //
  357. // Synopsis: Return a pointer to the first trailing space in [ptsz].
  358. //
  359. // History: 11-22-1996 DavidMun Created
  360. //
  361. //---------------------------------------------------------------------------
  362. LPCTSTR
  363. FindFirstTrailingSpace(LPCTSTR ptsz)
  364. {
  365. LPCTSTR ptszFirstTrailingSpace = NULL;
  366. LPCTSTR ptszCur;
  367. for (ptszCur = ptsz; *ptszCur; ptszCur= NextChar(ptszCur))
  368. {
  369. if (*ptszCur == ' ' || *ptszCur == '\t')
  370. {
  371. if (!ptszFirstTrailingSpace)
  372. {
  373. ptszFirstTrailingSpace = ptszCur;
  374. }
  375. }
  376. else if (ptszFirstTrailingSpace)
  377. {
  378. ptszFirstTrailingSpace = NULL;
  379. }
  380. }
  381. return ptszFirstTrailingSpace;
  382. }
  383. //+--------------------------------------------------------------------------
  384. //
  385. // Function: DeleteQuotes
  386. //
  387. // Synopsis: Delete all instances of the double quote character from
  388. // [ptsz].
  389. //
  390. // Arguments: [ptsz] - nul terminated string
  391. //
  392. // Modifies: *[ptsz]
  393. //
  394. // History: 11-21-1996 DavidMun Created
  395. //
  396. //---------------------------------------------------------------------------
  397. VOID
  398. DeleteQuotes(LPTSTR ptsz)
  399. {
  400. TCHAR *ptszLead;
  401. TCHAR *ptszTrail;
  402. //
  403. // Move a lead and trail pointer through the buffer, copying from the lead
  404. // to the trail whenever the character isn't one we're deleting (a double
  405. // quote).
  406. //
  407. // Note: the "Lead" and "Trail" in ptszLead and ptszTrail do not refer
  408. // to DBCS lead/trail bytes, rather that the ptszLead pointer can move
  409. // ahead of ptszTrail when it is advanced past a double quote.
  410. //
  411. for (ptszTrail = ptszLead = ptsz;
  412. *ptszLead;
  413. ptszLead = NextChar(ptszLead))
  414. {
  415. //
  416. // If the current char is a double quote, we want it deleted, so don't
  417. // copy it and go on to the next char.
  418. //
  419. if (*ptszLead == TEXT('"'))
  420. {
  421. continue;
  422. }
  423. //
  424. // ptszLead is pointing to a 'normal' character, i.e. not a double
  425. // quote.
  426. //
  427. // It might be the first byte of a two-byte DBCS char if we are
  428. // running on Win9x. Be sure to copy both bytes of such a character
  429. // (We're using the terms Lead and Trail in two different senses
  430. // here.)
  431. //
  432. if (IsLead(*ptszLead))
  433. {
  434. *ptszTrail++ = ptszLead[0];
  435. *ptszTrail++ = ptszLead[1];
  436. }
  437. else
  438. {
  439. *ptszTrail++ = ptszLead[0];
  440. }
  441. }
  442. *ptszTrail = TEXT('\0');
  443. }
  444. //+--------------------------------------------------------------------------
  445. //
  446. // Function: AddQuotes
  447. //
  448. // Synopsis: If there's room in the buffer, insert a quote as the first
  449. // character and concat a quote as the last character.
  450. //
  451. // Arguments: [ptsz] - string to modify
  452. // [cchBuf] - size of string's buffer, in chars
  453. //
  454. // History: 11-22-1996 DavidMun Created
  455. //
  456. //---------------------------------------------------------------------------
  457. VOID
  458. AddQuotes(LPTSTR ptsz, ULONG cchBuf)
  459. {
  460. ULONG cch = lstrlen(ptsz);
  461. if (cch < cchBuf - 2)
  462. {
  463. MoveMemory(ptsz + 1, ptsz, cch * sizeof(TCHAR));
  464. *ptsz = ptsz[cch + 1] = TEXT('"');
  465. ptsz[cch + 2] = TEXT('\0');
  466. }
  467. }
  468. //+---------------------------------------------------------------------------
  469. //
  470. // Function: FileExists
  471. //
  472. // Synopsis: Return TRUE if the specified file exists and is not a
  473. // directory.
  474. //
  475. // Arguments: [ptszFileName] - filename to search for & modify
  476. //
  477. // Modifies: Filename portion of [ptszFileName].
  478. //
  479. // Returns: TRUE - file found
  480. // FALSE - file not found or error
  481. //
  482. // History: 11-21-96 DavidMun Created
  483. //
  484. //----------------------------------------------------------------------------
  485. BOOL
  486. FileExists(LPTSTR ptszFileName)
  487. {
  488. TCHAR tszFullPath[MAX_PATH];
  489. LPTSTR ptszFilePart;
  490. ULONG cchFullPath = GetFullPathName(ptszFileName,
  491. MAX_PATH,
  492. tszFullPath,
  493. &ptszFilePart);
  494. if (cchFullPath && cchFullPath <= MAX_PATH)
  495. {
  496. lstrcpy(ptszFileName, tszFullPath);
  497. }
  498. ULONG flAttributes;
  499. flAttributes = GetFileAttributes(ptszFileName);
  500. // If we couldn't determine file's attributes, don't consider it found
  501. if (flAttributes == 0xFFFFFFFF)
  502. {
  503. return FALSE;
  504. }
  505. // if file is actually a directory, it's unsuitable as a task, so fail
  506. if (flAttributes & FILE_ATTRIBUTE_DIRECTORY)
  507. {
  508. return FALSE;
  509. }
  510. // Get the filename sans trailing spaces
  511. WIN32_FIND_DATA FindFileData;
  512. HANDLE hFile = FindFirstFile(ptszFileName, &FindFileData);
  513. if (hFile == INVALID_HANDLE_VALUE)
  514. {
  515. return FALSE;
  516. }
  517. FindClose(hFile);
  518. LPTSTR ptszLastSlash = _tcsrchr((LPTSTR)ptszFileName, TEXT('\\'));
  519. if (ptszLastSlash)
  520. {
  521. lstrcpy(ptszLastSlash + 1, FindFileData.cFileName);
  522. }
  523. return TRUE;
  524. }
  525. //+--------------------------------------------------------------------------
  526. //
  527. // Function: FileExistsInPath
  528. //
  529. // Synopsis: Return TRUE if [ptszFilename] exists in path [ptszPath].
  530. //
  531. // Arguments: [ptszFilename] - file to look for
  532. // [ptszPath] - semicolon delimited list of dirs
  533. // [ptszFoundFile] - if found, [ptszDir]\[ptszFilename]
  534. // [cchFoundBuf] - size in chars of [ptszFoundFile] buffer
  535. //
  536. // Returns: TRUE if file found in dir, FALSE otherwise
  537. //
  538. // Modifies: *[ptszFoundFile]
  539. //
  540. // History: 11-22-1996 DavidMun Created
  541. //
  542. // Notes: Note that by calling FileExists we ensure the found file
  543. // is a file, not a directory.
  544. //
  545. //---------------------------------------------------------------------------
  546. BOOL
  547. FileExistsInPath(
  548. LPTSTR ptszFilename,
  549. LPCTSTR ptszPath,
  550. LPTSTR ptszFoundFile,
  551. ULONG cchFoundBuf)
  552. {
  553. ULONG cchCopied;
  554. LPTSTR ptszFilePart;
  555. cchCopied = SearchPath(ptszPath,
  556. ptszFilename,
  557. NULL,
  558. cchFoundBuf,
  559. ptszFoundFile,
  560. &ptszFilePart);
  561. if (cchCopied && cchCopied <= cchFoundBuf)
  562. {
  563. return FileExists(ptszFoundFile);
  564. }
  565. return FALSE;
  566. }
  567. #define MAX_KEY_LEN (ARRAY_LEN(REGSTR_PATH_APPPATHS) + MAX_PATH)
  568. //+--------------------------------------------------------------------------
  569. //
  570. // Function: GetAppPathInfo
  571. //
  572. // Synopsis: Fill [ptszAppPathDefault] with the default value and
  573. // [ptszAppPathVar] with the Path value in the
  574. // [ptszFilename] application's key under the APPPATHS regkey.
  575. //
  576. // Arguments: [ptszFilename] - application name
  577. // [ptszAppPathDefault] - if not NULL, filled with default value
  578. // [cchDefaultBuf] - size of [ptszAppPathDefault] buffer
  579. // [ptszAppPathVar] - if not NULL, filled with Path value
  580. // [cchPathVarBuf] - size of [cchPathVarBuf] buffer
  581. //
  582. // Modifies: *[ptszAppPathDefault], *[ptszAppPathVar]
  583. //
  584. // History: 11-22-1996 DavidMun Created
  585. //
  586. // Notes: Both values are optional on the registry key, so if a
  587. // requested value isn't found, it is set to "".
  588. //
  589. //---------------------------------------------------------------------------
  590. VOID
  591. GetAppPathInfo(
  592. LPCTSTR ptszFilename,
  593. LPTSTR ptszAppPathDefault,
  594. ULONG cchDefaultBuf,
  595. LPTSTR ptszAppPathVar,
  596. ULONG cchPathVarBuf)
  597. {
  598. HKEY hkey = NULL;
  599. TCHAR tszAppPathKey[MAX_KEY_LEN];
  600. //
  601. // Initialize out vars
  602. //
  603. if (ptszAppPathDefault)
  604. {
  605. ptszAppPathDefault[0] = TEXT('\0');
  606. }
  607. if (ptszAppPathVar)
  608. {
  609. ptszAppPathVar[0] = TEXT('\0');
  610. }
  611. //
  612. // Build registry key name for this app
  613. //
  614. lstrcpy(tszAppPathKey, REGSTR_PATH_APPPATHS);
  615. lstrcat(tszAppPathKey, TEXT("\\"));
  616. lstrcat(tszAppPathKey, ptszFilename);
  617. do
  618. {
  619. LRESULT lr;
  620. lr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  621. tszAppPathKey,
  622. 0,
  623. KEY_QUERY_VALUE,
  624. &hkey);
  625. if (lr != ERROR_SUCCESS)
  626. {
  627. break;
  628. }
  629. //
  630. // If the key could be opened, attempt to read requested values.
  631. // Both are optional, so ignore errors.
  632. //
  633. DWORD cb;
  634. DWORD dwType;
  635. if (ptszAppPathDefault)
  636. {
  637. cb = cchDefaultBuf * sizeof(TCHAR);
  638. lr = RegQueryValueEx(hkey,
  639. NULL, // value name
  640. NULL, // reserved
  641. &dwType,
  642. (LPBYTE) ptszAppPathDefault,
  643. &cb);
  644. if (lr == ERROR_SUCCESS)
  645. {
  646. schAssert(dwType == REG_SZ);
  647. }
  648. }
  649. if (ptszAppPathVar)
  650. {
  651. cb = cchPathVarBuf * sizeof(TCHAR);
  652. lr = RegQueryValueEx(hkey,
  653. TEXT("Path"), // value name
  654. NULL, // reserved
  655. &dwType,
  656. (LPBYTE) ptszAppPathVar,
  657. &cb);
  658. if (lr == ERROR_SUCCESS)
  659. {
  660. schAssert(dwType == REG_SZ);
  661. }
  662. }
  663. } while (0);
  664. if (hkey)
  665. {
  666. RegCloseKey(hkey);
  667. }
  668. }