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.

1149 lines
31 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. fileutil.c
  5. Abstract:
  6. Implements utility routines for files, file paths, etc.
  7. Author:
  8. Jim Schmidt (jimschm) 08-Mar-2000
  9. Revision History:
  10. <alias> <date> <comments>
  11. --*/
  12. #include "pch.h"
  13. //
  14. // Includes
  15. //
  16. // None
  17. #define DBG_FILEUTIL "FileUtil"
  18. //
  19. // Strings
  20. //
  21. // None
  22. //
  23. // Constants
  24. //
  25. // None
  26. //
  27. // Macros
  28. //
  29. // None
  30. //
  31. // Types
  32. //
  33. // None
  34. //
  35. // Globals
  36. //
  37. // None
  38. //
  39. // Macro expansion list
  40. //
  41. // None
  42. //
  43. // Private function prototypes
  44. //
  45. // None
  46. //
  47. // Macro expansion definition
  48. //
  49. // None
  50. //
  51. // Code
  52. //
  53. BOOL
  54. pDefaultFindFileA (
  55. IN PCSTR FileName
  56. )
  57. {
  58. return (GetFileAttributesA (FileName) != INVALID_ATTRIBUTES);
  59. }
  60. BOOL
  61. pDefaultFindFileW (
  62. IN PCWSTR FileName
  63. )
  64. {
  65. return (GetFileAttributesW (FileName) != INVALID_ATTRIBUTES);
  66. }
  67. BOOL
  68. pDefaultSearchPathA (
  69. IN PCSTR FileName,
  70. IN DWORD BufferLength,
  71. OUT PSTR Buffer
  72. )
  73. {
  74. PSTR dontCare;
  75. return SearchPathA (NULL, FileName, NULL, BufferLength, Buffer, &dontCare);
  76. }
  77. BOOL
  78. pDefaultSearchPathW (
  79. IN PCWSTR FileName,
  80. IN DWORD BufferLength,
  81. OUT PWSTR Buffer
  82. )
  83. {
  84. PWSTR dontCare;
  85. return SearchPathW (NULL, FileName, NULL, BufferLength, Buffer, &dontCare);
  86. }
  87. PCMDLINEA
  88. ParseCmdLineExA (
  89. IN PCSTR CmdLine,
  90. IN PCSTR Separators, OPTIONAL
  91. IN PFINDFILEA FindFileCallback, OPTIONAL
  92. IN PSEARCHPATHA SearchPathCallback, OPTIONAL
  93. IN OUT PGROWBUFFER Buffer
  94. )
  95. {
  96. PFINDFILEA findFileCallback = FindFileCallback;
  97. PSEARCHPATHA searchPathCallback = SearchPathCallback;
  98. GROWBUFFER SpacePtrs = INIT_GROWBUFFER;
  99. PCSTR p;
  100. PSTR q;
  101. INT Count;
  102. INT i;
  103. INT j;
  104. PSTR *Array;
  105. PCSTR Start;
  106. CHAR OldChar = 0;
  107. GROWBUFFER StringBuf = INIT_GROWBUFFER;
  108. PBYTE CopyBuf;
  109. PCMDLINEA CmdLineTable;
  110. PCMDLINEARGA CmdLineArg;
  111. ULONG_PTR Base;
  112. PSTR Path = NULL;
  113. PSTR UnquotedPath = NULL;
  114. PSTR FixedFileName = NULL;
  115. PSTR FirstArgPath = NULL;
  116. DWORD pathSize = 0;
  117. PCSTR FullPath = NULL;
  118. BOOL fileExists = FALSE;
  119. PSTR CmdLineCopy;
  120. BOOL Quoted;
  121. UINT OriginalArgOffset = 0;
  122. UINT CleanedUpArgOffset = 0;
  123. BOOL GoodFileFound = FALSE;
  124. PSTR EndOfFirstArg;
  125. BOOL QuoteMode = FALSE;
  126. PSTR End;
  127. if (!Separators) {
  128. Separators = " =,;";
  129. }
  130. if (!findFileCallback) {
  131. findFileCallback = pDefaultFindFileA;
  132. }
  133. if (!searchPathCallback) {
  134. searchPathCallback = pDefaultSearchPathA;
  135. }
  136. pathSize = SizeOfStringA (CmdLine) * 2;
  137. if (pathSize < MAX_MBCHAR_PATH) {
  138. pathSize = MAX_MBCHAR_PATH;
  139. }
  140. Path = AllocTextA (pathSize);
  141. UnquotedPath = AllocTextA (pathSize);
  142. FixedFileName = AllocTextA (pathSize);
  143. FirstArgPath = AllocTextA (pathSize);
  144. CmdLineCopy = DuplicateTextA (CmdLine);
  145. if (!Path ||
  146. !UnquotedPath ||
  147. !FixedFileName ||
  148. !FirstArgPath ||
  149. !CmdLineCopy
  150. ) {
  151. return NULL;
  152. }
  153. //
  154. // Build an array of places to break the string
  155. //
  156. for (p = CmdLineCopy ; *p ; p = _mbsinc (p)) {
  157. if (_mbsnextc (p) == '\"') {
  158. QuoteMode = !QuoteMode;
  159. } else if (!QuoteMode &&
  160. _mbschr (Separators, _mbsnextc (p))
  161. ) {
  162. //
  163. // Remove excess spaces
  164. //
  165. q = (PSTR) p + 1;
  166. while (_mbsnextc (q) == ' ') {
  167. q++;
  168. }
  169. if (q > p + 1) {
  170. MoveMemory ((PBYTE) p + sizeof (CHAR), q, SizeOfStringA (q));
  171. }
  172. GbAppendPvoid (&SpacePtrs, p);
  173. }
  174. }
  175. //
  176. // Prepare the CMDLINE struct
  177. //
  178. CmdLineTable = (PCMDLINEA) GbGrow (Buffer, sizeof (CMDLINEA));
  179. MYASSERT (CmdLineTable);
  180. //
  181. // NOTE: We store string offsets, then at the end resolve them
  182. // to pointers later.
  183. //
  184. CmdLineTable->CmdLine = (PCSTR) (ULONG_PTR) StringBuf.End;
  185. GbMultiSzAppendA (&StringBuf, CmdLine);
  186. CmdLineTable->ArgCount = 0;
  187. //
  188. // Now test every combination, emulating CreateProcess
  189. //
  190. Count = SpacePtrs.End / sizeof (PVOID);
  191. Array = (PSTR *) SpacePtrs.Buf;
  192. i = -1;
  193. EndOfFirstArg = NULL;
  194. while (i < Count) {
  195. GoodFileFound = FALSE;
  196. Quoted = FALSE;
  197. if (i >= 0) {
  198. Start = Array[i] + 1;
  199. } else {
  200. Start = CmdLineCopy;
  201. }
  202. //
  203. // Check for a full path at Start
  204. //
  205. if (_mbsnextc (Start) != '/') {
  206. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  207. if (j < Count) {
  208. OldChar = *Array[j];
  209. *Array[j] = 0;
  210. }
  211. FullPath = Start;
  212. //
  213. // Remove quotes; continue in the loop if it has no terminating quotes
  214. //
  215. Quoted = FALSE;
  216. if (_mbsnextc (Start) == '\"') {
  217. StringCopyByteCountA (UnquotedPath, Start + 1, pathSize);
  218. q = _mbschr (UnquotedPath, '\"');
  219. if (q) {
  220. *q = 0;
  221. FullPath = UnquotedPath;
  222. Quoted = TRUE;
  223. } else {
  224. FullPath = NULL;
  225. }
  226. }
  227. if (FullPath && *FullPath) {
  228. //
  229. // Look in file system for the path
  230. //
  231. fileExists = findFileCallback (FullPath);
  232. if (!fileExists && EndOfFirstArg) {
  233. //
  234. // Try prefixing the path with the first arg's path.
  235. //
  236. StringCopyByteCountA (
  237. EndOfFirstArg,
  238. FullPath,
  239. pathSize - (HALF_PTR) ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
  240. );
  241. FullPath = FirstArgPath;
  242. fileExists = findFileCallback (FullPath);
  243. }
  244. if (!fileExists && i < 0) {
  245. //
  246. // Try appending .exe, then testing again. This
  247. // emulates what CreateProcess does.
  248. //
  249. StringCopyByteCountA (
  250. FixedFileName,
  251. FullPath,
  252. pathSize - sizeof (".exe")
  253. );
  254. q = GetEndOfStringA (FixedFileName);
  255. q = _mbsdec (FixedFileName, q);
  256. MYASSERT (q);
  257. if (_mbsnextc (q) != '.') {
  258. q = _mbsinc (q);
  259. }
  260. StringCopyA (q, ".exe");
  261. FullPath = FixedFileName;
  262. fileExists = findFileCallback (FullPath);
  263. }
  264. if (fileExists) {
  265. //
  266. // Full file path found. Test its file status, then
  267. // move on if there are no important operations on it.
  268. //
  269. OriginalArgOffset = StringBuf.End;
  270. GbMultiSzAppendA (&StringBuf, Start);
  271. if (!StringMatchA (Start, FullPath)) {
  272. CleanedUpArgOffset = StringBuf.End;
  273. GbMultiSzAppendA (&StringBuf, FullPath);
  274. } else {
  275. CleanedUpArgOffset = OriginalArgOffset;
  276. }
  277. i = j;
  278. GoodFileFound = TRUE;
  279. }
  280. }
  281. if (j < Count) {
  282. *Array[j] = OldChar;
  283. }
  284. }
  285. if (!GoodFileFound) {
  286. //
  287. // If a wack is in the path, then we could have a relative path, an arg, or
  288. // a full path to a non-existent file.
  289. //
  290. if (_mbschr (Start, '\\')) {
  291. #ifdef DEBUG
  292. j = i + 1;
  293. if (j < Count) {
  294. OldChar = *Array[j];
  295. *Array[j] = 0;
  296. }
  297. DEBUGMSGA ((
  298. DBG_VERBOSE,
  299. "%s is a non-existent path spec, a relative path, or an arg",
  300. Start
  301. ));
  302. if (j < Count) {
  303. *Array[j] = OldChar;
  304. }
  305. #endif
  306. } else {
  307. //
  308. // The string at Start did not contain a full path; try using
  309. // searchPathCallback.
  310. //
  311. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  312. if (j < Count) {
  313. OldChar = *Array[j];
  314. *Array[j] = 0;
  315. }
  316. FullPath = Start;
  317. //
  318. // Remove quotes; continue in the loop if it has no terminating quotes
  319. //
  320. Quoted = FALSE;
  321. if (_mbsnextc (Start) == '\"') {
  322. StringCopyByteCountA (UnquotedPath, Start + 1, pathSize);
  323. q = _mbschr (UnquotedPath, '\"');
  324. if (q) {
  325. *q = 0;
  326. FullPath = UnquotedPath;
  327. Quoted = TRUE;
  328. } else {
  329. FullPath = NULL;
  330. }
  331. }
  332. if (FullPath && *FullPath) {
  333. if (searchPathCallback (
  334. FullPath,
  335. pathSize / sizeof (Path[0]),
  336. Path
  337. )) {
  338. FullPath = Path;
  339. } else if (i < 0) {
  340. //
  341. // Try appending .exe and searching the path again
  342. //
  343. StringCopyByteCountA (
  344. FixedFileName,
  345. FullPath,
  346. pathSize - sizeof (".exe")
  347. );
  348. q = GetEndOfStringA (FixedFileName);
  349. q = _mbsdec (FixedFileName, q);
  350. MYASSERT (q);
  351. if (_mbsnextc (q) != '.') {
  352. q = _mbsinc (q);
  353. }
  354. StringCopyA (q, ".exe");
  355. if (searchPathCallback (
  356. FixedFileName,
  357. pathSize / sizeof (Path[0]),
  358. Path
  359. )) {
  360. FullPath = Path;
  361. } else {
  362. FullPath = NULL;
  363. }
  364. } else {
  365. FullPath = NULL;
  366. }
  367. }
  368. if (FullPath && *FullPath) {
  369. fileExists = findFileCallback (FullPath);
  370. MYASSERT (fileExists);
  371. OriginalArgOffset = StringBuf.End;
  372. GbMultiSzAppendA (&StringBuf, Start);
  373. if (!StringMatchA (Start, FullPath)) {
  374. CleanedUpArgOffset = StringBuf.End;
  375. GbMultiSzAppendA (&StringBuf, FullPath);
  376. } else {
  377. CleanedUpArgOffset = OriginalArgOffset;
  378. }
  379. i = j;
  380. GoodFileFound = TRUE;
  381. }
  382. if (j < Count) {
  383. *Array[j] = OldChar;
  384. }
  385. }
  386. }
  387. }
  388. }
  389. CmdLineTable->ArgCount += 1;
  390. CmdLineArg = (PCMDLINEARGA) GbGrow (Buffer, sizeof (CMDLINEARGA));
  391. MYASSERT (CmdLineArg);
  392. if (GoodFileFound) {
  393. //
  394. // We have a good full file spec in FullPath, its existance
  395. // is in fileExists, and i has been moved to the space beyond
  396. // the path. We now add a table entry.
  397. //
  398. CmdLineArg->OriginalArg = (PCSTR) (ULONG_PTR) OriginalArgOffset;
  399. CmdLineArg->CleanedUpArg = (PCSTR) (ULONG_PTR) CleanedUpArgOffset;
  400. CmdLineArg->Quoted = Quoted;
  401. if (!EndOfFirstArg) {
  402. StringCopyByteCountA (
  403. FirstArgPath,
  404. (PCSTR) (StringBuf.Buf + (ULONG_PTR) CmdLineArg->CleanedUpArg),
  405. pathSize
  406. );
  407. q = (PSTR) GetFileNameFromPathA (FirstArgPath);
  408. if (q) {
  409. q = _mbsdec (FirstArgPath, q);
  410. if (q) {
  411. *q = 0;
  412. }
  413. }
  414. EndOfFirstArg = AppendWackA (FirstArgPath);
  415. }
  416. } else {
  417. //
  418. // We do not have a good file spec; we must have a non-file
  419. // argument. Put it in the table, and advance to the next
  420. // arg.
  421. //
  422. j = i + 1;
  423. if (j <= Count) {
  424. if (j < Count) {
  425. OldChar = *Array[j];
  426. *Array[j] = 0;
  427. }
  428. CmdLineArg->OriginalArg = (PCSTR) (ULONG_PTR) StringBuf.End;
  429. GbMultiSzAppendA (&StringBuf, Start);
  430. Quoted = FALSE;
  431. if (_mbschr (Start, '\"')) {
  432. p = Start;
  433. q = UnquotedPath;
  434. End = (PSTR) ((PBYTE) UnquotedPath + pathSize - sizeof (CHAR));
  435. while (*p && q < End) {
  436. if (IsLeadByte (p)) {
  437. *q++ = *p++;
  438. *q++ = *p++;
  439. } else {
  440. if (*p == '\"') {
  441. p++;
  442. } else {
  443. *q++ = *p++;
  444. }
  445. }
  446. }
  447. *q = 0;
  448. CmdLineArg->CleanedUpArg = (PCSTR) (ULONG_PTR) StringBuf.End;
  449. GbMultiSzAppendA (&StringBuf, UnquotedPath);
  450. Quoted = TRUE;
  451. } else {
  452. CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
  453. }
  454. CmdLineArg->Quoted = Quoted;
  455. if (j < Count) {
  456. *Array[j] = OldChar;
  457. }
  458. i = j;
  459. }
  460. }
  461. }
  462. //
  463. // We now have a command line table; transfer StringBuf to Buffer, then
  464. // convert all offsets into pointers.
  465. //
  466. MYASSERT (StringBuf.End);
  467. CopyBuf = GbGrow (Buffer, StringBuf.End);
  468. MYASSERT (CopyBuf);
  469. Base = (ULONG_PTR) CopyBuf;
  470. CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
  471. // Earlier GbGrow may have moved the buffer in memory. We need to repoint CmdLineTable
  472. CmdLineTable = (PCMDLINEA)Buffer->Buf;
  473. CmdLineTable->CmdLine = (PCSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
  474. CmdLineArg = &CmdLineTable->Args[0];
  475. for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
  476. CmdLineArg->OriginalArg = (PCSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
  477. CmdLineArg->CleanedUpArg = (PCSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
  478. CmdLineArg++;
  479. }
  480. GbFree (&StringBuf);
  481. GbFree (&SpacePtrs);
  482. FreeTextA (CmdLineCopy);
  483. FreeTextA (FirstArgPath);
  484. FreeTextA (FixedFileName);
  485. FreeTextA (UnquotedPath);
  486. FreeTextA (Path);
  487. return (PCMDLINEA) Buffer->Buf;
  488. }
  489. PCMDLINEW
  490. ParseCmdLineExW (
  491. IN PCWSTR CmdLine,
  492. IN PCWSTR Separators, OPTIONAL
  493. IN PFINDFILEW FindFileCallback, OPTIONAL
  494. IN PSEARCHPATHW SearchPathCallback, OPTIONAL
  495. IN OUT PGROWBUFFER Buffer
  496. )
  497. {
  498. PFINDFILEW findFileCallback = FindFileCallback;
  499. PSEARCHPATHW searchPathCallback = SearchPathCallback;
  500. GROWBUFFER SpacePtrs = INIT_GROWBUFFER;
  501. PCWSTR p;
  502. PWSTR q;
  503. INT Count;
  504. INT i;
  505. INT j;
  506. PWSTR *Array;
  507. PCWSTR Start;
  508. WCHAR OldChar = 0;
  509. GROWBUFFER StringBuf = INIT_GROWBUFFER;
  510. PBYTE CopyBuf;
  511. PCMDLINEW CmdLineTable;
  512. PCMDLINEARGW CmdLineArg;
  513. ULONG_PTR Base;
  514. PWSTR Path = NULL;
  515. PWSTR UnquotedPath = NULL;
  516. PWSTR FixedFileName = NULL;
  517. PWSTR FirstArgPath = NULL;
  518. DWORD pathSize = 0;
  519. PCWSTR FullPath = NULL;
  520. BOOL fileExists = FALSE;
  521. PWSTR CmdLineCopy;
  522. BOOL Quoted;
  523. UINT OriginalArgOffset = 0;
  524. UINT CleanedUpArgOffset = 0;
  525. BOOL GoodFileFound = FALSE;
  526. PWSTR EndOfFirstArg;
  527. BOOL QuoteMode = FALSE;
  528. PWSTR End;
  529. if (!Separators) {
  530. Separators = L" =,;";
  531. }
  532. if (!findFileCallback) {
  533. findFileCallback = pDefaultFindFileW;
  534. }
  535. if (!searchPathCallback) {
  536. searchPathCallback = pDefaultSearchPathW;
  537. }
  538. pathSize = SizeOfStringW (CmdLine);
  539. if (pathSize < MAX_WCHAR_PATH) {
  540. pathSize = MAX_WCHAR_PATH;
  541. }
  542. Path = AllocTextW (pathSize);
  543. UnquotedPath = AllocTextW (pathSize);
  544. FixedFileName = AllocTextW (pathSize);
  545. FirstArgPath = AllocTextW (pathSize);
  546. CmdLineCopy = DuplicateTextW (CmdLine);
  547. if (!Path ||
  548. !UnquotedPath ||
  549. !FixedFileName ||
  550. !FirstArgPath ||
  551. !CmdLineCopy
  552. ) {
  553. return NULL;
  554. }
  555. //
  556. // Build an array of places to break the string
  557. //
  558. for (p = CmdLineCopy ; *p ; p++) {
  559. if (*p == L'\"') {
  560. QuoteMode = !QuoteMode;
  561. } else if (!QuoteMode &&
  562. wcschr (Separators, *p)
  563. ) {
  564. //
  565. // Remove excess spaces
  566. //
  567. q = (PWSTR) p + 1;
  568. while (*q == L' ') {
  569. q++;
  570. }
  571. if (q > p + 1) {
  572. MoveMemory ((PBYTE) p + sizeof (WCHAR), q, SizeOfStringW (q));
  573. }
  574. GbAppendPvoid (&SpacePtrs, p);
  575. }
  576. }
  577. //
  578. // Prepare the CMDLINE struct
  579. //
  580. CmdLineTable = (PCMDLINEW) GbGrow (Buffer, sizeof (CMDLINEW));
  581. MYASSERT (CmdLineTable);
  582. //
  583. // NOTE: We store string offsets, then at the end resolve them
  584. // to pointers later.
  585. //
  586. CmdLineTable->CmdLine = (PCWSTR) (ULONG_PTR) StringBuf.End;
  587. GbMultiSzAppendW (&StringBuf, CmdLine);
  588. CmdLineTable->ArgCount = 0;
  589. //
  590. // Now test every combination, emulating CreateProcess
  591. //
  592. Count = SpacePtrs.End / sizeof (PVOID);
  593. Array = (PWSTR *) SpacePtrs.Buf;
  594. i = -1;
  595. EndOfFirstArg = NULL;
  596. while (i < Count) {
  597. GoodFileFound = FALSE;
  598. Quoted = FALSE;
  599. if (i >= 0) {
  600. Start = Array[i] + 1;
  601. } else {
  602. Start = CmdLineCopy;
  603. }
  604. //
  605. // Check for a full path at Start
  606. //
  607. if (*Start != L'/') {
  608. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  609. if (j < Count) {
  610. OldChar = *Array[j];
  611. *Array[j] = 0;
  612. }
  613. FullPath = Start;
  614. //
  615. // Remove quotes; continue in the loop if it has no terminating quotes
  616. //
  617. Quoted = FALSE;
  618. if (*Start == L'\"') {
  619. StringCopyByteCountW (UnquotedPath, Start + 1, pathSize);
  620. q = wcschr (UnquotedPath, L'\"');
  621. if (q) {
  622. *q = 0;
  623. FullPath = UnquotedPath;
  624. Quoted = TRUE;
  625. } else {
  626. FullPath = NULL;
  627. }
  628. }
  629. if (FullPath && *FullPath) {
  630. //
  631. // Look in file system for the path
  632. //
  633. fileExists = findFileCallback (FullPath);
  634. if (!fileExists && EndOfFirstArg) {
  635. //
  636. // Try prefixing the path with the first arg's path.
  637. //
  638. StringCopyByteCountW (
  639. EndOfFirstArg,
  640. FullPath,
  641. pathSize - (HALF_PTR) ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
  642. );
  643. FullPath = FirstArgPath;
  644. fileExists = findFileCallback (FullPath);
  645. }
  646. if (!fileExists && i < 0) {
  647. //
  648. // Try appending .exe, then testing again. This
  649. // emulates what CreateProcess does.
  650. //
  651. StringCopyByteCountW (
  652. FixedFileName,
  653. FullPath,
  654. pathSize - sizeof (L".exe")
  655. );
  656. q = GetEndOfStringW (FixedFileName);
  657. q--;
  658. MYASSERT (q >= FixedFileName);
  659. if (*q != L'.') {
  660. q++;
  661. }
  662. StringCopyW (q, L".exe");
  663. FullPath = FixedFileName;
  664. fileExists = findFileCallback (FullPath);
  665. }
  666. if (fileExists) {
  667. //
  668. // Full file path found. Test its file status, then
  669. // move on if there are no important operations on it.
  670. //
  671. OriginalArgOffset = StringBuf.End;
  672. GbMultiSzAppendW (&StringBuf, Start);
  673. if (!StringMatchW (Start, FullPath)) {
  674. CleanedUpArgOffset = StringBuf.End;
  675. GbMultiSzAppendW (&StringBuf, FullPath);
  676. } else {
  677. CleanedUpArgOffset = OriginalArgOffset;
  678. }
  679. i = j;
  680. GoodFileFound = TRUE;
  681. }
  682. }
  683. if (j < Count) {
  684. *Array[j] = OldChar;
  685. }
  686. }
  687. if (!GoodFileFound) {
  688. //
  689. // If a wack is in the path, then we could have a relative path, an arg, or
  690. // a full path to a non-existent file.
  691. //
  692. if (wcschr (Start, L'\\')) {
  693. #ifdef DEBUG
  694. j = i + 1;
  695. if (j < Count) {
  696. OldChar = *Array[j];
  697. *Array[j] = 0;
  698. }
  699. DEBUGMSGW ((
  700. DBG_VERBOSE,
  701. "%s is a non-existent path spec, a relative path, or an arg",
  702. Start
  703. ));
  704. if (j < Count) {
  705. *Array[j] = OldChar;
  706. }
  707. #endif
  708. } else {
  709. //
  710. // The string at Start did not contain a full path; try using
  711. // searchPathCallback.
  712. //
  713. for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
  714. if (j < Count) {
  715. OldChar = *Array[j];
  716. *Array[j] = 0;
  717. }
  718. FullPath = Start;
  719. //
  720. // Remove quotes; continue in the loop if it has no terminating quotes
  721. //
  722. Quoted = FALSE;
  723. if (*Start == L'\"') {
  724. StringCopyByteCountW (UnquotedPath, Start + 1, pathSize);
  725. q = wcschr (UnquotedPath, L'\"');
  726. if (q) {
  727. *q = 0;
  728. FullPath = UnquotedPath;
  729. Quoted = TRUE;
  730. } else {
  731. FullPath = NULL;
  732. }
  733. }
  734. if (FullPath && *FullPath) {
  735. if (searchPathCallback (
  736. FullPath,
  737. pathSize / sizeof (Path[0]),
  738. Path
  739. )) {
  740. FullPath = Path;
  741. } else if (i < 0) {
  742. //
  743. // Try appending .exe and searching the path again
  744. //
  745. StringCopyByteCountW (
  746. FixedFileName,
  747. FullPath,
  748. pathSize - sizeof (L".exe")
  749. );
  750. q = GetEndOfStringW (FixedFileName);
  751. q--;
  752. MYASSERT (q >= FixedFileName);
  753. if (*q != L'.') {
  754. q++;
  755. }
  756. StringCopyW (q, L".exe");
  757. if (searchPathCallback (
  758. FixedFileName,
  759. pathSize / sizeof (Path[0]),
  760. Path
  761. )) {
  762. FullPath = Path;
  763. } else {
  764. FullPath = NULL;
  765. }
  766. } else {
  767. FullPath = NULL;
  768. }
  769. }
  770. if (FullPath && *FullPath) {
  771. fileExists = findFileCallback (FullPath);
  772. MYASSERT (fileExists);
  773. OriginalArgOffset = StringBuf.End;
  774. GbMultiSzAppendW (&StringBuf, Start);
  775. if (!StringMatchW (Start, FullPath)) {
  776. CleanedUpArgOffset = StringBuf.End;
  777. GbMultiSzAppendW (&StringBuf, FullPath);
  778. } else {
  779. CleanedUpArgOffset = OriginalArgOffset;
  780. }
  781. i = j;
  782. GoodFileFound = TRUE;
  783. }
  784. if (j < Count) {
  785. *Array[j] = OldChar;
  786. }
  787. }
  788. }
  789. }
  790. }
  791. CmdLineTable->ArgCount += 1;
  792. CmdLineArg = (PCMDLINEARGW) GbGrow (Buffer, sizeof (CMDLINEARGW));
  793. MYASSERT (CmdLineArg);
  794. if (GoodFileFound) {
  795. //
  796. // We have a good full file spec in FullPath, its existance
  797. // is in fileExists, and i has been moved to the space beyond
  798. // the path. We now add a table entry.
  799. //
  800. CmdLineArg->OriginalArg = (PCWSTR) (ULONG_PTR) OriginalArgOffset;
  801. CmdLineArg->CleanedUpArg = (PCWSTR) (ULONG_PTR) CleanedUpArgOffset;
  802. CmdLineArg->Quoted = Quoted;
  803. if (!EndOfFirstArg) {
  804. StringCopyByteCountW (
  805. FirstArgPath,
  806. (PCWSTR) (StringBuf.Buf + (ULONG_PTR) CmdLineArg->CleanedUpArg),
  807. pathSize
  808. );
  809. q = (PWSTR) GetFileNameFromPathW (FirstArgPath);
  810. if (q) {
  811. q--;
  812. if (q >= FirstArgPath) {
  813. *q = 0;
  814. }
  815. }
  816. EndOfFirstArg = AppendWackW (FirstArgPath);
  817. }
  818. } else {
  819. //
  820. // We do not have a good file spec; we must have a non-file
  821. // argument. Put it in the table, and advance to the next
  822. // arg.
  823. //
  824. j = i + 1;
  825. if (j <= Count) {
  826. if (j < Count) {
  827. OldChar = *Array[j];
  828. *Array[j] = 0;
  829. }
  830. CmdLineArg->OriginalArg = (PCWSTR) (ULONG_PTR) StringBuf.End;
  831. GbMultiSzAppendW (&StringBuf, Start);
  832. Quoted = FALSE;
  833. if (wcschr (Start, '\"')) {
  834. p = Start;
  835. q = UnquotedPath;
  836. End = (PWSTR) ((PBYTE) UnquotedPath + pathSize - sizeof (WCHAR));
  837. while (*p && q < End) {
  838. if (*p == L'\"') {
  839. p++;
  840. } else {
  841. *q++ = *p++;
  842. }
  843. }
  844. *q = 0;
  845. CmdLineArg->CleanedUpArg = (PCWSTR) (ULONG_PTR) StringBuf.End;
  846. GbMultiSzAppendW (&StringBuf, UnquotedPath);
  847. Quoted = TRUE;
  848. } else {
  849. CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
  850. }
  851. CmdLineArg->Quoted = Quoted;
  852. if (j < Count) {
  853. *Array[j] = OldChar;
  854. }
  855. i = j;
  856. }
  857. }
  858. }
  859. //
  860. // We now have a command line table; transfer StringBuf to Buffer, then
  861. // convert all offsets into pointers.
  862. //
  863. MYASSERT (StringBuf.End);
  864. CopyBuf = GbGrow (Buffer, StringBuf.End);
  865. MYASSERT (CopyBuf);
  866. Base = (ULONG_PTR) CopyBuf;
  867. CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
  868. // Earlier GbGrow may have moved the buffer in memory. We need to repoint CmdLineTable
  869. CmdLineTable = (PCMDLINEW)Buffer->Buf;
  870. CmdLineTable->CmdLine = (PCWSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
  871. CmdLineArg = &CmdLineTable->Args[0];
  872. for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
  873. CmdLineArg->OriginalArg = (PCWSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
  874. CmdLineArg->CleanedUpArg = (PCWSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
  875. CmdLineArg++;
  876. }
  877. GbFree (&StringBuf);
  878. GbFree (&SpacePtrs);
  879. FreeTextW (CmdLineCopy);
  880. FreeTextW (FirstArgPath);
  881. FreeTextW (FixedFileName);
  882. FreeTextW (UnquotedPath);
  883. FreeTextW (Path);
  884. return (PCMDLINEW) Buffer->Buf;
  885. }