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.

613 lines
13 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. BOOL APIENTRY
  4. IsStringInList(
  5. LPWSTR lpS,
  6. LPWSTR lpList
  7. )
  8. {
  9. while (*lpList) {
  10. if (!_wcsicmp(lpS,lpList)) {
  11. return(TRUE);
  12. }
  13. lpList += wcslen(lpList) + 1;
  14. }
  15. return FALSE;
  16. }
  17. LPWSTR APIENTRY
  18. SheRemoveQuotesW(
  19. LPWSTR sz)
  20. {
  21. LPWSTR lpT;
  22. if (WCHAR_QUOTE == *sz) {
  23. for (lpT = sz+1; *lpT && WCHAR_QUOTE != *lpT; lpT++) {
  24. *(lpT-1) = *lpT;
  25. }
  26. if (WCHAR_QUOTE == *lpT) {
  27. *(lpT-1) = WCHAR_NULL;
  28. }
  29. }
  30. return(sz);
  31. }
  32. LPSTR APIENTRY
  33. SheRemoveQuotesA(
  34. LPSTR sz)
  35. {
  36. LPSTR lpT;
  37. if (CHAR_QUOTE == *sz) {
  38. for (lpT = sz+1; *lpT && CHAR_QUOTE != *lpT; lpT++) {
  39. *(lpT-1) = *lpT;
  40. #if (defined(DBCS) || defined(FE_SB))
  41. if (IsDBCSLeadByte(*lpT)) {
  42. lpT++;
  43. *(lpT-1) = *lpT;
  44. }
  45. #endif
  46. }
  47. if (CHAR_QUOTE == *lpT) {
  48. *(lpT-1) = CHAR_NULL;
  49. }
  50. }
  51. return(sz);
  52. }
  53. /////////////////////////////////////////////////////////////////////
  54. //
  55. // Name: SheShortenPathA
  56. //
  57. // Synopsis: Thunk to ShortenPathW
  58. //
  59. /////////////////////////////////////////////////////////////////////
  60. BOOL APIENTRY
  61. SheShortenPathA(LPSTR pPath, BOOL bShorten)
  62. {
  63. WCHAR pPathW[MAX_PATH];
  64. BOOL bRetVal;
  65. MultiByteToWideChar(CP_ACP, 0, pPath, -1, pPathW, MAX_PATH);
  66. bRetVal = SheShortenPathW(pPathW, bShorten);
  67. WideCharToMultiByte(CP_ACP, 0, pPathW, -1, pPath, MAX_PATH,
  68. NULL, NULL);
  69. return bRetVal;
  70. }
  71. /////////////////////////////////////////////////////////////////////
  72. //
  73. // Name: SheShortenPath
  74. //
  75. // Synopsis: Takes a pathname and converts all dirs to shortnames/longnames
  76. //
  77. // INOUT: lpszPath -- Path to shorten/lengthen (May be in DQUOTES)
  78. // Must not be a commandline!
  79. //
  80. // bShorten -- T=shorten, F=Lengthen
  81. //
  82. // Return: BOOL T=Converted,
  83. // F=ran out of space, buffer left alone
  84. //
  85. //
  86. // Assumes: lpszPath takes the form {"}?:\{f\}*f{"} or {"}\\f\f\{f\}*f{"}
  87. // COUNTOF pSrc buffer >= MAXPATHELN
  88. //
  89. // Effects: Strips quotes out of pPath, if any
  90. //
  91. //
  92. // Notes:
  93. //
  94. /////////////////////////////////////////////////////////////////////
  95. BOOL APIENTRY
  96. SheShortenPathW(LPWSTR pPath, BOOL bShorten)
  97. {
  98. WCHAR szDest[MAX_PATH];
  99. LPWSTR pSrcNextSpec, pReplaceSpec;
  100. LPWSTR pDest, pNewName, p;
  101. LPWSTR pSrc;
  102. DWORD cchPathOffset;
  103. HANDLE hFind;
  104. WIN32_FIND_DATA FindData;
  105. UINT i;
  106. INT nSpaceLeft = MAX_PATH-1;
  107. pSrc = pPath;
  108. //
  109. // Eliminate d-quotes
  110. //
  111. for (p = pDest = pSrc; *p; p++, pDest++) {
  112. if (WCHAR_QUOTE == *p)
  113. p++;
  114. *pDest = *p;
  115. }
  116. *pDest = WCHAR_NULL;
  117. //
  118. // Strip out leading spaces
  119. //
  120. while (WCHAR_SPACE == *pSrc)
  121. pSrc++;
  122. //
  123. // Initialize pNewName so it is calculated once.
  124. //
  125. pNewName = bShorten ?
  126. FindData.cAlternateFileName :
  127. FindData.cFileName;
  128. //
  129. // Skip past \\foo\bar or <drive>:
  130. //
  131. pDest = szDest;
  132. pSrcNextSpec = pSrc;
  133. // reuse shell32 internal api that calculates path
  134. // offset. cchPathOffset will be the offset that when
  135. // added to the pointer will result in a pointer to the
  136. // backslash before the first part of the path
  137. cchPathOffset = SheGetPathOffsetW(pSrc);
  138. // Check to see if it's valid. If pSrc is not of the \\foo\bar
  139. // or <drive>: form we just do nothing
  140. if (0xFFFFFFFF == cchPathOffset) {
  141. return TRUE;
  142. }
  143. // cchPathOffset will then always be atleast 1 and is the
  144. // number of characters - 1 that we want to copy (that is, if 0
  145. // was permissible, it would denote 1 character).
  146. do {
  147. *pDest++ = *pSrcNextSpec++;
  148. if (!--nSpaceLeft)
  149. return FALSE;
  150. } while (cchPathOffset--);
  151. //
  152. // At this point, we have just the filenames that we can shorten:
  153. // \\foo\bar\it\is\here -> it\is\here
  154. // c:\angry\lions -> angry\lions
  155. //
  156. while(pSrcNextSpec) {
  157. //
  158. // pReplaceSpec holds the current spec we need to replace.
  159. // By default, if we can't find the altname, then just use this.
  160. //
  161. pReplaceSpec = pSrcNextSpec;
  162. //
  163. // Search for trailing "\"
  164. // pSrcNextSpec will point to the next spec to fix (*pSrcNextSpec=NULL if done)
  165. //
  166. for(;*pSrcNextSpec && WCHAR_BSLASH != *pSrcNextSpec; pSrcNextSpec++)
  167. ;
  168. if (*pSrcNextSpec) {
  169. //
  170. // If there is more, then pSrcNextSpec should point to it.
  171. // Also delimit this spec.
  172. //
  173. *pSrcNextSpec = WCHAR_NULL;
  174. } else {
  175. pSrcNextSpec = NULL;
  176. }
  177. hFind = FindFirstFile(pSrc, &FindData);
  178. //
  179. // We could exit as soon as this FindFirstFileFails,
  180. // but there's the special case of having execute
  181. // without read permission. This would fail since the lfn
  182. // is valid for lfn apps.
  183. //
  184. if (INVALID_HANDLE_VALUE != hFind) {
  185. FindClose(hFind);
  186. if (pNewName[0]) {
  187. //
  188. // We have found an altname.
  189. // Use it instead.
  190. //
  191. pReplaceSpec = pNewName;
  192. }
  193. }
  194. i = wcslen(pReplaceSpec);
  195. nSpaceLeft -= i;
  196. if (nSpaceLeft <= 0)
  197. return FALSE;
  198. wcscpy(pDest, pReplaceSpec);
  199. pDest+=i;
  200. //
  201. // Now replace the WCHAR_NULL with a slash if necessary
  202. //
  203. if (pSrcNextSpec) {
  204. *pSrcNextSpec++ = WCHAR_BSLASH;
  205. //
  206. // Also add backslash to dest
  207. //
  208. *pDest++ = WCHAR_BSLASH;
  209. nSpaceLeft--;
  210. }
  211. }
  212. wcscpy(pPath, szDest);
  213. return TRUE;
  214. }
  215. /*
  216. * Reads the list of program strings from win.ini
  217. */
  218. LPWSTR GetPrograms()
  219. {
  220. static LPWSTR lpPrograms = WCHAR_NULL;
  221. LPWSTR lpT,lpS;
  222. if (lpPrograms) {
  223. return lpPrograms;
  224. }
  225. if (!(lpPrograms = (LPWSTR)LocalAlloc(LPTR, (MAX_PATH+1) * sizeof(WCHAR)))) {
  226. return(NULL);
  227. } else {
  228. GetProfileString(L"windows",L"programs",WSTR_BLANK,lpPrograms,MAX_PATH);
  229. for (lpS = lpT = lpPrograms; *lpT; lpT++) {
  230. if (*lpT == WCHAR_SPACE) {
  231. while (*lpT == WCHAR_SPACE) {
  232. lpT++;
  233. }
  234. lpT--;
  235. *lpS++ = 0;
  236. } else {
  237. *lpS++ = *lpT;
  238. }
  239. }
  240. *lpS++ = WCHAR_NULL;
  241. *lpS++ = WCHAR_NULL;
  242. return(lpPrograms);
  243. }
  244. }
  245. /*
  246. * Determines if an extension is a program
  247. */
  248. BOOL IsProgram(LPWSTR lpExt)
  249. {
  250. LPWSTR lpPrograms = GetPrograms();
  251. return lpPrograms ? IsStringInList(lpExt,lpPrograms) : FALSE;
  252. }
  253. /* finds a file along the path. Returns the error code or 0 if success.
  254. */
  255. WORD
  256. SearchForFile(
  257. LPCWSTR lpDir,
  258. LPWSTR lpFile,
  259. LPWSTR lpFullPath,
  260. DWORD cchFullPath,
  261. LPWSTR lpExt)
  262. {
  263. LPWSTR lpT;
  264. LPWSTR lpD;
  265. LPWSTR lpExts;
  266. WCHAR szFile[MAX_PATH+1];
  267. DWORD cchPath;
  268. if (*lpFile == WCHAR_QUOTE) {
  269. lpFile = SheRemoveQuotes(lpFile);
  270. }
  271. if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_BSLASH))) {
  272. ++lpT;
  273. } else if (NULL != (lpT=StrRChrW(lpFile, NULL, WCHAR_COLON))) {
  274. ++lpT;
  275. } else {
  276. lpT = lpFile;
  277. }
  278. if (NULL != (lpT=StrRChrW(lpT, NULL, WCHAR_DOT))) {
  279. int n;
  280. n = wcslen(lpT + 1);
  281. StrCpyN(lpExt, lpT+1, n < 64 ? n+1 : 65); // max extension
  282. } else {
  283. *lpExt = WCHAR_NULL;
  284. }
  285. // If there's no extension then just use programs list don't
  286. // try searc hing for the app sans extension. This fixes the bogus
  287. // file.run stuff.
  288. if (!*lpExt) {
  289. goto UseDefExts;
  290. }
  291. //
  292. // NOTE: Do NOT call CharUpper for any of the strings in this routine.
  293. // It will cause problems for the Turkish locale.
  294. //
  295. cchPath = SearchPath(lpDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  296. if (!cchPath) {
  297. cchPath = SearchPath(NULL, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  298. }
  299. if (cchPath >= cchFullPath) {
  300. return(SE_ERR_OOM);
  301. }
  302. if (cchPath == 0) {
  303. return(SE_ERR_FNF);
  304. }
  305. CheckEscapes(lpFullPath, cchFullPath);
  306. return 0;
  307. UseDefExts:
  308. wcscpy(szFile,lpFile);
  309. lpFile = szFile;
  310. wcscat(lpFile,WSTR_DOT);
  311. lpD = lpFile + wcslen(lpFile);
  312. if (NULL != (lpExts = GetPrograms())) {
  313. // We want to pass through the loop twice checking whether the
  314. // file is in lpDir first, and then if it's in the sysdirs, via SearchPath(NULL, ...)
  315. // Add some state and extend the while loop
  316. LPCWSTR lpTempDir = lpDir;
  317. LPWSTR lpTempExts = lpExts;
  318. BOOL bCheckedSysDirs = FALSE;
  319. while (*lpTempExts || !bCheckedSysDirs) {
  320. // After the first pass, lpTempExts will be NULL
  321. // Reset it and loop through again with lpTempDir = NULL so that
  322. // SearchPath looks at the system dirs
  323. if (!*lpTempExts) {
  324. bCheckedSysDirs = TRUE;
  325. lpTempExts = lpExts;
  326. lpTempDir = NULL;
  327. }
  328. wcscpy(lpD,lpTempExts);
  329. wcscpy(lpExt,lpTempExts);
  330. cchPath = SearchPath(lpTempDir, lpFile, NULL, cchFullPath, lpFullPath, &lpT);
  331. if (cchPath >= cchFullPath) {
  332. return(SE_ERR_OOM);
  333. }
  334. if (cchPath != 0) {
  335. CheckEscapes(lpFullPath, cchFullPath);
  336. return 0;
  337. }
  338. lpTempExts += wcslen(lpTempExts)+1;
  339. }
  340. }
  341. return(SE_ERR_FNF);
  342. }
  343. /////////////////////////////////////////////////////////////////////
  344. //
  345. // Name: QualifyAppName
  346. //
  347. // Synopsis: Creates a fully qualified path to the app in a commandline
  348. //
  349. // INC lpCmdLine Command line to qualify
  350. // (Must have DQuotes if has spaces)
  351. // OUT lpImage Fully qualified result
  352. // OUT ppArgs Pointer to args in lpCmdLine, _incl_ leading space
  353. // OPTIONAL
  354. //
  355. // Return: DWORD length of path, 0 = fail
  356. //
  357. //
  358. // Assumes: len of executable in lpCmdLine is < MAX_PATH
  359. // len of exts are < 64
  360. //
  361. // Effects:
  362. //
  363. //
  364. // Notes:
  365. //
  366. /////////////////////////////////////////////////////////////////////
  367. DWORD
  368. QualifyAppName(
  369. IN LPCWSTR lpCmdLine,
  370. OUT LPWSTR lpImage,
  371. OPTIONAL OUT LPCWSTR* ppArgs)
  372. {
  373. LPWSTR lpAppName;
  374. BOOL bAppNameInQuotes = FALSE;
  375. DWORD cch = 0;
  376. lpAppName = lpImage;
  377. // sanity check
  378. if (!lpCmdLine) {
  379. return(0);
  380. }
  381. while (*lpCmdLine &&
  382. (*lpCmdLine != WCHAR_SPACE || bAppNameInQuotes)) {
  383. if (*lpCmdLine == WCHAR_QUOTE) {
  384. bAppNameInQuotes = !bAppNameInQuotes;
  385. lpCmdLine++;
  386. continue;
  387. }
  388. *lpAppName++ = *lpCmdLine++;
  389. cch++;
  390. }
  391. *lpAppName = WCHAR_NULL;
  392. //
  393. // Save the pointer to the argument list
  394. //
  395. if (ppArgs) {
  396. *ppArgs = lpCmdLine;
  397. }
  398. if (SheGetPathOffsetW(lpImage) == -1) {
  399. WCHAR szTemp[MAX_PATH];
  400. lstrcpy((LPWSTR)szTemp, lpImage);
  401. if (StrChrW(lpImage, WCHAR_DOT)) {
  402. LPWSTR lpFileName;
  403. return(SearchPath(NULL, szTemp, NULL, MAX_PATH, lpImage, &lpFileName));
  404. }
  405. else {
  406. WCHAR szExt[65];
  407. *lpImage = WCHAR_NULL;
  408. if (SearchForFile(NULL, (LPWSTR)szTemp, lpImage, MAX_PATH, szExt)) {
  409. return(0);
  410. }
  411. return(lstrlen(lpImage));
  412. }
  413. }
  414. return(cch);
  415. }
  416. BOOL
  417. SheConvertPathW(
  418. LPWSTR lpCmdLine,
  419. LPWSTR lpFile,
  420. UINT cchCmdBuf)
  421. /*++
  422. Routine Description:
  423. Takes a command line and file and shortens both if the app in the
  424. command line is dos/wow.
  425. Returns: BOOL T=converted
  426. Arguments:
  427. INOUT lpCmdLine Command line to test
  428. exe must be in DQuotes if it has spaces,
  429. on return, will have DQuotes if necessary
  430. INOUT lpFile Fully qualified file to shorten
  431. May be in DQuotes, but on return will not
  432. have DQuotes (since single file)
  433. IN cchCmdBuf Size of buffer in characters
  434. Return Value:
  435. VOID, but lpFile shortened (in place) if lpCmdLine is dos/wow.
  436. There are pathalogoical "lfns" (Single unicode chars) that can
  437. actually get longer when they are shortened. In this case, we
  438. won't AV, but we will truncate the parms!
  439. // Qualify path assumes that the second parm is a buffer of
  440. // size atleast MAX_PATH, which is nicely equivalent to MAX_PATH
  441. // needs cleanup!
  442. --*/
  443. {
  444. LPWSTR lpszFullPath;
  445. LONG lBinaryType;
  446. BOOL bInQuote = FALSE;
  447. LPWSTR lpArgs;
  448. UINT cchNewLen;
  449. BOOL bRetVal = FALSE;
  450. lpszFullPath = (LPWSTR) LocalAlloc(LMEM_FIXED,
  451. cchCmdBuf*sizeof(*lpCmdLine));
  452. if (!lpszFullPath)
  453. return bRetVal;
  454. //
  455. // We must do the swap here since we need to copy the
  456. // parms back to lpCmdLine.
  457. //
  458. lstrcpy(lpszFullPath, lpCmdLine);
  459. if (QualifyAppName(lpszFullPath, lpCmdLine, &lpArgs)) {
  460. if (!GetBinaryType(lpCmdLine, &lBinaryType) ||
  461. lBinaryType == SCS_DOS_BINARY ||
  462. lBinaryType == SCS_WOW_BINARY) {
  463. SheShortenPath(lpCmdLine, TRUE);
  464. if (lpFile) {
  465. SheShortenPath(lpFile, TRUE);
  466. }
  467. bRetVal = TRUE;
  468. }
  469. //
  470. // Must readd quotes
  471. //
  472. CheckEscapes(lpCmdLine, cchCmdBuf);
  473. cchNewLen = lstrlen(lpCmdLine);
  474. StrNCpy(lpCmdLine+cchNewLen, lpArgs, cchCmdBuf-cchNewLen);
  475. } else {
  476. //
  477. // QualifyAppName failed, restore the command line back
  478. // to the original state.
  479. //
  480. lstrcpy(lpCmdLine, lpszFullPath);
  481. }
  482. LocalFree((HLOCAL)lpszFullPath);
  483. return bRetVal;
  484. }