Leaked source code of windows server 2003
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.

1934 lines
63 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cext.c
  5. Abstract:
  6. External command support
  7. --*/
  8. #include "cmd.h"
  9. #define DBENV 0x0080
  10. #define DBENVSCAN 0x0010
  11. unsigned start_type ; /* D64 */
  12. extern UINT CurrentCP;
  13. extern TCHAR Fmt16[] ; /* @@5h */
  14. extern unsigned DosErr ;
  15. extern BOOL CtrlCSeen;
  16. extern TCHAR CurDrvDir[] ;
  17. extern TCHAR CmdExt[], BatExt[], PathStr[] ;
  18. extern TCHAR PathExtStr[], PathExtDefaultStr[];
  19. extern TCHAR ComSpec[] ; /* M033 - Use ComSpec for SM memory */
  20. extern TCHAR ComSpecStr[] ; /* M033 - Use ComSpec for SM memory */
  21. extern void tokshrink(TCHAR*);
  22. extern TCHAR PathChar ;
  23. extern TCHAR SwitChar ;
  24. extern PTCHAR pszTitleCur;
  25. extern BOOLEAN fTitleChanged;
  26. extern int LastRetCode ;
  27. extern HANDLE PipePid ; /* M024 - Store PID from piped cmd */
  28. extern struct envdata CmdEnv ; // Holds info to manipulate Cmd's environment
  29. extern int glBatType; // to distinguish OS/2 vs DOS errorlevel behavior depending on a script file name
  30. TCHAR szNameEqExitCodeEnvVar[] = TEXT ("=ExitCode");
  31. TCHAR szNameEqExitCodeAsciiEnvVar[] = TEXT ("=ExitCodeAscii");
  32. TCHAR ShellOpenCommandString[] = TEXT( "\\Shell\\Open\\Command" );
  33. WORD
  34. GetProcessSubsystemType(
  35. HANDLE hProcess
  36. );
  37. /*** ExtCom - controls the execution of external programs
  38. *
  39. * Purpose:
  40. * Synchronously execute an external command. Call ECWork with the
  41. * appropriate values to have this done.
  42. *
  43. * ExtCom(struct cmdnode *n)
  44. *
  45. * Args:
  46. * Parse tree node containing the command to be executed.
  47. *
  48. * Returns:
  49. * Whatever ECWork returns.
  50. *
  51. * Notes:
  52. * During batch processing, labels are ignored. Empty commands are
  53. * also ignored.
  54. *
  55. */
  56. int ExtCom(n)
  57. struct cmdnode *n ;
  58. {
  59. if (CurrentBatchFile && *n->cmdline == COLON)
  60. return(SUCCESS) ;
  61. if (n && n->cmdline && mystrlen(n->cmdline)) {
  62. return(ECWork(n, AI_SYNC, CW_W_YES)) ; /* M024 */
  63. } ;
  64. return(SUCCESS) ;
  65. }
  66. /********************* START OF SPECIFICATION **************************/
  67. /* */
  68. /* SUBROUTINE NAME: ECWork */
  69. /* */
  70. /* DESCRIPTIVE NAME: Execute External Commands Worker */
  71. /* */
  72. /* FUNCTION: Execute External Commands */
  73. /* This routine calls SearchForExecutable routine to search */
  74. /* for the executable command. If the command ( .EXE, .COM, */
  75. /* or .CMD file ) is found, the command is executed. */
  76. /* */
  77. /* ENTRY POINT: ECWork */
  78. /* LINKAGE: NEAR */
  79. /* */
  80. /* INPUT: n - the parse tree node containing the command to be executed*/
  81. /* */
  82. /* ai - the asynchronous indicator */
  83. /* - 0 = Exec synchronous with parent */
  84. /* - 1 = Exec asynchronous and discard child return code */
  85. /* - 2 = Exec asynchronous and save child return code */
  86. /* */
  87. /* wf - the wait flag */
  88. /* - 0 = Wait for process completion */
  89. /* - 1 = Return immediately (Pipes) */
  90. /* */
  91. /* OUTPUT: None. */
  92. /* */
  93. /* EXIT-NORMAL: */
  94. /* If synchronous execution, the return code of the command is */
  95. /* returned. */
  96. /* */
  97. /* If asynchronous execution, the return code of the exec call */
  98. /* is returned. */
  99. /* */
  100. /* EXIT-ERROR: */
  101. /* Return FAILURE to the caller. */
  102. /* */
  103. /* */
  104. /********************** END OF SPECIFICATION **************************/
  105. /*** ECWork - begins the execution of external commands
  106. *
  107. * Purpose:
  108. * To search for and execute an external command. Update LastRetCode
  109. * if an external program was executed.
  110. *
  111. * int ECWork(struct cmdnode *n, unsigned ai, unsigned wf)
  112. *
  113. * Args:
  114. * n - the parse tree node containing the command to be executed
  115. * ai - the asynchronous indicator
  116. * - 0 = Exec synchronous with parent
  117. * - 1 = Exec asynchronous and discard child return code
  118. * - 2 = Exec asynchronous and save child return code
  119. * wf - the wait flag
  120. * - 0 = Wait for process completion
  121. * - 1 = Return immediately (Pipes)
  122. *
  123. * Returns:
  124. * If syncronous execution, the return code of the command is returned.
  125. * If asyncronous execution, the return code of the exec call is returned.
  126. *
  127. * Notes:
  128. * The pid of a program that will be waited on is placed in the global
  129. * variable Retcds.ptcod so that WaitProc can use it and so SigHand()
  130. * can kill it if necessary (only during SYNC exec's).
  131. * M024 - Added wait flag parm so pipes can get immediate return while
  132. * still doing an AI_KEEP async exec.
  133. * - Considerable revisions to structure.
  134. */
  135. int ECWork(n, ai, wf)
  136. struct cmdnode *n ;
  137. unsigned ai ;
  138. unsigned wf ;
  139. {
  140. TCHAR *fnptr, /* Ptr to filename */
  141. *argptr, /* Command Line String */
  142. *tptr; /* M034 - Temps */
  143. int i ; /* Work variable */
  144. TCHAR * onb ; /* M035 - Obj name buffer */
  145. ULONG rc;
  146. if (!(fnptr = mkstr(MAX_PATH*sizeof(TCHAR)+sizeof(TCHAR)))) /* Loc/nam of file to exec */
  147. return(FAILURE) ;
  148. argptr = GetTitle( n );
  149. tptr = argptr;
  150. if (argptr == NULL) {
  151. return( FAILURE );
  152. }
  153. i = SearchForExecutable(n, fnptr) ;
  154. if (i == SFE_ISEXECOM) { /* Found .com or .exe file */
  155. if (!(onb = (TCHAR *)mkstr(MAX_PATH*sizeof(TCHAR)+sizeof(TCHAR)))) /* M035 */
  156. {
  157. return(FAILURE) ;
  158. }
  159. SetConTitle( tptr );
  160. rc = ExecPgm( n, onb, ai, wf, argptr, fnptr, tptr);
  161. ResetConTitle( pszTitleCur );
  162. return( rc );
  163. } ;
  164. if (i == SFE_ISBAT) { /* Found .cmd file, call BatProc */
  165. SetConTitle(tptr);
  166. rc = BatProc(n, fnptr, BT_CHN) ;
  167. ResetConTitle( pszTitleCur );
  168. return(rc) ;
  169. } ;
  170. if (i == SFE_ISDIR) {
  171. return ChangeDir2(fnptr, TRUE);
  172. }
  173. LastRetCode = DosErr;
  174. if (i == SFE_FAIL) { /* Out of Memory Error */
  175. return(FAILURE) ;
  176. } ;
  177. if (DosErr == MSG_DIR_BAD_COMMAND_OR_FILE) {
  178. PutStdErr(DosErr, ONEARG, n->cmdline);
  179. }
  180. else {
  181. PutStdErr(DosErr, NOARGS);
  182. }
  183. return(FAILURE) ;
  184. }
  185. #ifndef WIN95_CMD
  186. VOID
  187. RestoreCurrentDirectories( VOID )
  188. /* this routine sets the current process's current directories to
  189. those of the child if the child was the VDM to fix DOS batch files.
  190. */
  191. {
  192. ULONG cchCurDirs;
  193. UINT PreviousErrorMode;
  194. PTCHAR pCurDirs,pCurCurDir;
  195. BOOL CurDrive=TRUE;
  196. #ifdef UNICODE
  197. PCHAR pT;
  198. #endif
  199. cchCurDirs = GetVDMCurrentDirectories( 0,
  200. NULL
  201. );
  202. if (cchCurDirs == 0)
  203. return;
  204. pCurDirs = gmkstr(cchCurDirs*sizeof(TCHAR));
  205. #ifdef UNICODE
  206. pT = gmkstr(cchCurDirs);
  207. #endif
  208. GetVDMCurrentDirectories( cchCurDirs,
  209. #ifdef UNICODE
  210. pT
  211. #else
  212. pCurDirs
  213. #endif
  214. );
  215. #ifdef UNICODE
  216. MultiByteToWideChar(CurrentCP, MB_PRECOMPOSED, pT, -1, pCurDirs, cchCurDirs);
  217. #endif
  218. // set error mode so we don't get popup if trying to set curdir
  219. // on empty floppy drive
  220. PreviousErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  221. for (pCurCurDir=pCurDirs;*pCurCurDir!=NULLC;pCurCurDir+=(_tcslen(pCurCurDir)+1)) {
  222. ChangeDir2(pCurCurDir,CurDrive);
  223. CurDrive=FALSE;
  224. }
  225. SetErrorMode(PreviousErrorMode);
  226. //free(pCurDirs);
  227. #ifdef UNICODE
  228. FreeStr((PTCHAR)pT);
  229. #endif
  230. }
  231. #endif // WIN95_CMD
  232. /********************* START OF SPECIFICATION **************************/
  233. /* */
  234. /* SUBROUTINE NAME: ExecPgm */
  235. /* */
  236. /* DESCRIPTIVE NAME: Call DosExecPgm to execute External Command */
  237. /* */
  238. /* FUNCTION: Execute External Commands with DosExecPgm */
  239. /* This routine calls DosExecPgm to execute the command. */
  240. /* */
  241. /* */
  242. /* NOTES: This is a new routine added for OS/2 1.1 release. */
  243. /* */
  244. /* */
  245. /* ENTRY POINT: ExecPgm */
  246. /* LINKAGE: NEAR */
  247. /* */
  248. /* INPUT: n - the parse tree node containing the command to be executed*/
  249. /* */
  250. /* ai - the asynchronous indicator */
  251. /* - 0 = Exec synchronous with parent */
  252. /* - 1 = Exec asynchronous and discard child return code */
  253. /* - 2 = Exec asynchronous and save child return code */
  254. /* */
  255. /* wf - the wait flag */
  256. /* - 0 = Wait for process completion */
  257. /* - 1 = Return immediately (Pipes) */
  258. /* */
  259. /* OUTPUT: None. */
  260. /* */
  261. /* EXIT-NORMAL: */
  262. /* If synchronous execution, the return code of the command is */
  263. /* returned. */
  264. /* */
  265. /* If asynchronous execution, the return code of the exec call */
  266. /* is returned. */
  267. /* */
  268. /* EXIT-ERROR: */
  269. /* Return FAILURE to the caller. */
  270. /* */
  271. /* EFFECTS: */
  272. /* */
  273. /* INTERNAL REFERENCES: */
  274. /* ROUTINES: */
  275. /* ExecError - Handles execution error */
  276. /* PutStdErr - Print an error message */
  277. /* WaitProc - wait for the termination of the specified process, */
  278. /* its child process, and related pipelined */
  279. /* processes. */
  280. /* */
  281. /* */
  282. /* EXTERNAL REFERENCES: */
  283. /* ROUTINES: */
  284. /* DOSEXECPGM - Execute the specified program */
  285. /* DOSSMSETTITLE - Set title for the presentation manager */
  286. /* */
  287. /********************** END OF SPECIFICATION **************************/
  288. int
  289. ExecPgm(
  290. IN struct cmdnode *n,
  291. IN TCHAR *onb,
  292. IN unsigned int ai,
  293. IN unsigned int wf,
  294. IN TCHAR * argptr,
  295. IN TCHAR * fnptr,
  296. IN TCHAR * tptr
  297. )
  298. {
  299. int i ; /* Work variable */
  300. BOOL b;
  301. BOOL VdmProcess = FALSE;
  302. BOOL WowProcess = FALSE;
  303. LPTSTR CopyCmdValue;
  304. HANDLE hChildProcess;
  305. HDESK hdesk;
  306. LPTSTR lpDesktop;
  307. DWORD cbDesktop = 0;
  308. HWINSTA hwinsta;
  309. DWORD cbWinsta = 0;
  310. TCHAR szValEqExitCodeEnvVar [20];
  311. TCHAR szValEqExitCodeAsciiEnvVar [12];
  312. STARTUPINFO StartupInfo;
  313. PROCESS_INFORMATION ChildProcessInfo;
  314. memset( &StartupInfo, 0, sizeof( StartupInfo ) );
  315. StartupInfo.cb = sizeof( StartupInfo );
  316. StartupInfo.lpTitle = tptr;
  317. StartupInfo.dwX = 0;
  318. StartupInfo.dwY = 1;
  319. StartupInfo.dwXSize = 100;
  320. StartupInfo.dwYSize = 100;
  321. StartupInfo.dwFlags = 0;//STARTF_SHELLOVERRIDE;
  322. StartupInfo.wShowWindow = SW_SHOWNORMAL;
  323. // Pass current Desktop to a new process.
  324. hwinsta = GetProcessWindowStation();
  325. GetUserObjectInformation( hwinsta, UOI_NAME, NULL, 0, &cbWinsta );
  326. hdesk = GetThreadDesktop ( GetCurrentThreadId() );
  327. GetUserObjectInformation (hdesk, UOI_NAME, NULL, 0, &cbDesktop);
  328. if ((lpDesktop = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cbDesktop + cbWinsta + 32) ) != NULL ) {
  329. LPTSTR p = lpDesktop;
  330. if ( GetUserObjectInformation (hwinsta, UOI_NAME, p, cbWinsta, &cbWinsta) ) {
  331. if (cbWinsta > 0) {
  332. p += ((cbWinsta/sizeof(TCHAR))-1);
  333. *p++ = L'\\';
  334. }
  335. if ( GetUserObjectInformation (hdesk, UOI_NAME, p, cbDesktop, &cbDesktop) ) {
  336. StartupInfo.lpDesktop = lpDesktop;
  337. }
  338. }
  339. }
  340. //
  341. // Incredibly ugly hack for Win95 compatibility.
  342. //
  343. // XCOPY in Win95 reaches up into it's parent process to see if it was
  344. // invoked in a batch file. If so, then XCOPY pretends COPYCMD=/Y
  345. //
  346. // There is no way we can do this for NT. The best that can be done is
  347. // to detect if we're in a batch file starting XCOPY and then temporarily
  348. // change COPYCMD=/Y.
  349. //
  350. {
  351. const TCHAR *p = MyGetEnvVarPtr( TEXT( "COPYCMD" ));
  352. if (p == NULL) {
  353. p = TEXT( "" );
  354. }
  355. CopyCmdValue = malloc( (mystrlen( p ) + 1) * sizeof( TCHAR ));
  356. if (CopyCmdValue == NULL) {
  357. PutStdErr( MSG_NO_MEMORY, NOARGS );
  358. return FAILURE;
  359. }
  360. mystrcpy( CopyCmdValue, p );
  361. if ((SingleBatchInvocation
  362. || SingleCommandInvocation
  363. || CurrentBatchFile )
  364. && (p = mystrrchr( fnptr, TEXT( '\\' ))) != NULL
  365. && !lstrcmp( p, TEXT( "\\XCOPY.EXE" ))
  366. ) {
  367. SetEnvVar( TEXT( "COPYCMD" ), TEXT( "/Y" ) );
  368. }
  369. }
  370. //
  371. // If the restricted token exists then create the process with the
  372. // restricted token.
  373. // Else create the process without any restrictions.
  374. //
  375. if ((CurrentBatchFile != NULL) && (CurrentBatchFile->hRestrictedToken != NULL)) {
  376. b = CreateProcessAsUser( CurrentBatchFile->hRestrictedToken,
  377. fnptr,
  378. argptr,
  379. (LPSECURITY_ATTRIBUTES) NULL,
  380. (LPSECURITY_ATTRIBUTES) NULL,
  381. TRUE,
  382. 0,
  383. NULL,
  384. CurDrvDir,
  385. &StartupInfo,
  386. &ChildProcessInfo
  387. );
  388. } else {
  389. b = CreateProcess( fnptr,
  390. argptr,
  391. (LPSECURITY_ATTRIBUTES) NULL,
  392. (LPSECURITY_ATTRIBUTES) NULL,
  393. TRUE,
  394. 0,
  395. NULL,
  396. CurDrvDir,
  397. &StartupInfo,
  398. &ChildProcessInfo
  399. );
  400. }
  401. if (!b) {
  402. DosErr = i = GetLastError();
  403. } else {
  404. hChildProcess = ChildProcessInfo.hProcess;
  405. CloseHandle(ChildProcessInfo.hThread);
  406. }
  407. //
  408. // Undo ugly hack
  409. //
  410. SetEnvVar( TEXT( "COPYCMD" ), CopyCmdValue );
  411. free( CopyCmdValue );
  412. HeapFree (GetProcessHeap(), 0, lpDesktop);
  413. if (!b) {
  414. if (fEnableExtensions && DosErr == ERROR_BAD_EXE_FORMAT) {
  415. SHELLEXECUTEINFO sei;
  416. memset(&sei, 0, sizeof(sei));
  417. //
  418. // Use the DDEWAIT flag so apps can finish their DDE conversation
  419. // before ShellExecuteEx returns. Otherwise, apps like Word will
  420. // complain when they try to exit, confusing the user.
  421. //
  422. sei.cbSize = sizeof(sei);
  423. sei.fMask = SEE_MASK_HASTITLE |
  424. SEE_MASK_NO_CONSOLE |
  425. SEE_MASK_FLAG_DDEWAIT |
  426. SEE_MASK_NOCLOSEPROCESS;
  427. sei.lpFile = fnptr;
  428. sei.lpParameters = n->argptr;
  429. sei.lpDirectory = CurDrvDir;
  430. sei.nShow = StartupInfo.wShowWindow;
  431. try {
  432. b = ShellExecuteEx( &sei );
  433. if (b) {
  434. hChildProcess = sei.hProcess;
  435. leave;
  436. }
  437. if (sei.hInstApp == NULL) {
  438. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  439. } else if ((DWORD_PTR)sei.hInstApp == HINSTANCE_ERROR) {
  440. DosErr = ERROR_FILE_NOT_FOUND;
  441. } else {
  442. DosErr = HandleToUlong(sei.hInstApp);
  443. }
  444. } except( DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER ) {
  445. b = FALSE;
  446. }
  447. }
  448. if (!b) {
  449. mystrcpy( onb, fnptr );
  450. ExecError( onb ) ;
  451. return (FAILURE) ;
  452. }
  453. }
  454. #ifndef WIN95_CMD
  455. VdmProcess = ((UINT_PTR)(hChildProcess) & 1) ? TRUE : FALSE;
  456. WowProcess = ((UINT_PTR)(hChildProcess) & 2) ? TRUE : FALSE;
  457. #endif // WIN95_CMD
  458. if (hChildProcess == NULL
  459. || (fEnableExtensions
  460. && CurrentBatchFile == 0
  461. && !SingleBatchInvocation
  462. && !SingleCommandInvocation
  463. && ai == AI_SYNC
  464. && (WowProcess
  465. || GetProcessSubsystemType(hChildProcess) == IMAGE_SUBSYSTEM_WINDOWS_GUI
  466. )
  467. )
  468. ) {
  469. //
  470. // If extensions enabled and doing a synchronous exec of a GUI
  471. // application, then change it into an asynchronous exec, with the
  472. // return code discarded, ala Win 3.x and Win 95 COMMAND.COM
  473. //
  474. ai = AI_DSCD;
  475. }
  476. i = SUCCESS;
  477. start_type = EXECPGM;
  478. //
  479. // Now that the process has been started, process the various
  480. // termination conditions
  481. //
  482. if (ai == AI_SYNC) {
  483. //
  484. // Synchronous exec: we wait for the child, establish status code
  485. // environment variable, and update current directories if we're
  486. // running a DOS app
  487. //
  488. LastRetCode = WaitProc( hChildProcess );
  489. hChildProcess = NULL;
  490. i = LastRetCode ;
  491. //
  492. // Set up exit code environment variable both numeric and
  493. // ascii displayable
  494. //
  495. _stprintf (szValEqExitCodeEnvVar, TEXT("%08X"), i);
  496. SetEnvVar(szNameEqExitCodeEnvVar, szValEqExitCodeEnvVar );
  497. if ( (i >= 0x20) && (i <= 0x7e) ) {
  498. _stprintf (szValEqExitCodeAsciiEnvVar, TEXT("%01C"), i);
  499. SetEnvVar(szNameEqExitCodeAsciiEnvVar, szValEqExitCodeAsciiEnvVar );
  500. } else
  501. SetEnvVar(szNameEqExitCodeAsciiEnvVar, TEXT("\0") );
  502. //
  503. // Update current directories if the process was handled by NTVDM. This
  504. // is done since DOS .BAT scripts allow inheritance of current directory
  505. // from child apps
  506. //
  507. #ifndef WIN95_CMD
  508. if (VdmProcess) {
  509. RestoreCurrentDirectories();
  510. }
  511. #endif // WIN95_CMD
  512. } else if (ai == AI_DSCD) {
  513. //
  514. // Disconnected exec; we simply close the process handle and
  515. // let it run to completion.
  516. //
  517. if (hChildProcess != NULL) {
  518. CloseHandle( hChildProcess );
  519. hChildProcess = NULL;
  520. }
  521. } else if (ai == AI_KEEP) {
  522. //
  523. // Keep-the-handle-open exec: this is called only within the
  524. // piping process. We've started the process, now we set a global
  525. // so that the piping code can pick it up.
  526. //
  527. PipePid = hChildProcess;
  528. } else {
  529. #if !defined( WIN95_CMD ) && DBG
  530. DbgBreakPoint( );
  531. #endif
  532. }
  533. return (i) ; /* i == return from DOSEXECPGM */
  534. }
  535. /********************* START OF SPECIFICATION **************************/
  536. /* */
  537. /* SUBROUTINE NAME: SearchForExecutable */
  538. /* */
  539. /* DESCRIPTIVE NAME: Search for Executable File */
  540. /* */
  541. /* FUNCTION: This routine searches the specified executable file. */
  542. /* If the file extension is specified, */
  543. /* CMD.EXE searches for the file with the file extension */
  544. /* to execute. If the specified file with the extension */
  545. /* is not found, CMD.EXE will display an error message to */
  546. /* indicate that the file is not found. */
  547. /* If the file extension is not specified, */
  548. /* CMD.EXE searches for the file with the order of these */
  549. /* file extensions, .COM, .EXE, .CMD, and .BAT. */
  550. /* The file which is found first will be executed. */
  551. /* */
  552. /* NOTES: 1) If a path is given, only the specified directory is */
  553. /* searched. */
  554. /* 2) If no path is given, the current directory of the */
  555. /* drive specified is searched followed by the */
  556. /* directories in the PATH environment variable. */
  557. /* 3) If no executable file is found, an error message is */
  558. /* printed. */
  559. /* */
  560. /* ENTRY POINT: SearchForExecutable */
  561. /* LINKAGE: NEAR */
  562. /* */
  563. /* INPUT: */
  564. /* n - parse tree node containing the command to be searched for */
  565. /* loc - the string in which the location of the command is to be */
  566. /* placed */
  567. /* */
  568. /* OUTPUT: None. */
  569. /* */
  570. /* EXIT-NORMAL: */
  571. /* Returns: */
  572. /* SFE_EXECOM, if a .EXE or .COM file is found. */
  573. /* SFE_ISBAT, if a .CMD file is found. */
  574. /* SFE_ISDIR, if a directory is found. */
  575. /* SFE_NOTFND, if no executable file is found. */
  576. /* */
  577. /* EXIT-ERROR: */
  578. /* Return FAILURE or */
  579. /* SFE_FAIL, if out of memory. */
  580. /* */
  581. /* EFFECTS: None. */
  582. /* */
  583. /* INTERNAL REFERENCES: */
  584. /* ROUTINES: */
  585. /* DoFind - Find the specified file. */
  586. /* GetEnvVar - Get full path. */
  587. /* FullPath - build a full path name. */
  588. /* TokStr - tokenize argument strings. */
  589. /* mkstr - allocate space for a string. */
  590. /* */
  591. /* EXTERNAL REFERENCES: */
  592. /* ROUTINES: */
  593. /* None */
  594. /* */
  595. /********************** END OF SPECIFICATION **************************/
  596. SearchForExecutable(n, loc)
  597. struct cmdnode *n ;
  598. TCHAR *loc ;
  599. {
  600. TCHAR *tokpath ;
  601. TCHAR *extPath ;
  602. TCHAR *extPathWrk ;
  603. TCHAR *fname;
  604. TCHAR *p1;
  605. TCHAR *tmps01;
  606. TCHAR *tmps02 = NULL;
  607. TCHAR pcstr[3];
  608. LONG BinaryType;
  609. size_t cName; // number of characters in file name.
  610. int tplen; // Length of the current tokpath token
  611. int dotloc; // loc offset where extension is appended
  612. int pcloc; // M014 - Flag. True=user had partial path
  613. int addpchar; // True - append PathChar to string @@5g
  614. TCHAR *j ;
  615. TCHAR wrkcmdline[MAX_PATH] ;
  616. unsigned tokpathlen;
  617. BOOL DoDot;
  618. //
  619. // Test for name too long first. If it is, we avoid wasting time
  620. //
  621. p1 = StripQuotes( n->cmdline );
  622. if ((cName = mystrlen(p1)) >= MAX_PATH) {
  623. DosErr = MSG_LINES_TOO_LONG;
  624. return(SFE_NOTFND) ;
  625. }
  626. //
  627. // If cmd extensions enable, then treat CMD without an extension
  628. // or path as a reference to COMSPEC variable. Guarantees we dont
  629. // get a random copy of CMD.EXE
  630. //
  631. if (fEnableExtensions && (p1 == NULL || !_tcsnicmp( p1, TEXT( "cmd " ), 4))) {
  632. p1 = GetEnvVar( ComSpecStr );
  633. if (p1 == NULL) {
  634. DosErr = MSG_INVALID_COMSPEC;
  635. return SFE_NOTFND;
  636. }
  637. }
  638. mytcsnset(wrkcmdline, NULLC, MAX_PATH);
  639. mystrcpy(wrkcmdline, p1);
  640. FixPChar( wrkcmdline, SwitChar );
  641. //
  642. // Create the path character string, this will be search string
  643. //
  644. pcstr[0] = PathChar;
  645. pcstr[1] = COLON;
  646. pcstr[2] = NULLC;
  647. //
  648. // The variable pcloc is used as a flag to indicate whether the user
  649. // did or didn't specify a drive or a partial path in his original
  650. // input. It will be NZ if drive or path was specified.
  651. //
  652. pcloc = ((mystrcspn(wrkcmdline,pcstr)) < cName) ;
  653. pcstr[1] = NULLC ; // Fixup pathchar string
  654. //
  655. // handle the case of the user typing in a file name of
  656. // ".", "..", or ending in "\"
  657. // pcloc true say string has to have either a pathchar or colon
  658. //
  659. if ( pcloc ) {
  660. if (!(p1 = mystrrchr( wrkcmdline, PathChar ))) {
  661. p1 = mystrchr( wrkcmdline, COLON );
  662. }
  663. p1++; // move to terminator if hanging ":" or "\"
  664. } else {
  665. p1 = wrkcmdline;
  666. }
  667. //
  668. // p1 is guaranteed to be non-zero
  669. //
  670. if ( !(*p1) || !_tcscmp( p1, TEXT(".") ) || !_tcscmp( p1, TEXT("..") ) ) {
  671. //
  672. // If CMD.EXE extensions enable, see if name matches
  673. // subdirectory name.
  674. //
  675. if (fEnableExtensions) {
  676. DWORD dwFileAttributes;
  677. dwFileAttributes = GetFileAttributes( loc );
  678. if (dwFileAttributes != 0xFFFFFFFF &&
  679. (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
  680. ) {
  681. return(SFE_ISDIR);
  682. }
  683. if (dwFileAttributes == 0xFFFFFFFF
  684. && GetLastError() == ERROR_NOT_READY) {
  685. DosErr = ERROR_NOT_READY;
  686. return SFE_FAIL;
  687. }
  688. }
  689. DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
  690. return(SFE_NOTFND) ;
  691. }
  692. if (!(tmps01 = mkstr(2*sizeof(TCHAR)*MAX_PATH))) {
  693. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  694. return(SFE_FAIL) ;
  695. }
  696. //
  697. // Handle the case of file..ext on a fat drive
  698. //
  699. mystrcpy( loc, wrkcmdline );
  700. loc[ &p1[0] - &wrkcmdline[0] ] = 0;
  701. mystrcat( loc, TEXT(".") );
  702. //
  703. // Check for a malformed name
  704. //
  705. if (FullPath(tmps01, loc,MAX_PATH*2)) {
  706. //
  707. // If CMD.EXE extensions enable, see if name matches
  708. // subdirectory name.
  709. //
  710. if (fEnableExtensions) {
  711. DWORD dwFileAttributes;
  712. dwFileAttributes = GetFileAttributes( loc );
  713. if (dwFileAttributes != 0xFFFFFFFF &&
  714. (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
  715. ) {
  716. return(SFE_ISDIR);
  717. }
  718. }
  719. DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
  720. return(SFE_NOTFND);
  721. }
  722. if ( *lastc(tmps01) != PathChar ) {
  723. mystrcat( tmps01, TEXT("\\") );
  724. }
  725. //
  726. // tmps01 contains full path + file name
  727. //
  728. mystrcat( tmps01, p1 );
  729. tmps01 = resize(tmps01, (mystrlen(tmps01)+1)*sizeof(TCHAR)) ;
  730. if (tmps01 == NULL) {
  731. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  732. return( SFE_FAIL ) ;
  733. }
  734. //
  735. // fname will point to last '\'
  736. // tmps01 is to be path and fname is to be name
  737. //
  738. fname = mystrrchr(tmps01,PathChar) ;
  739. *fname++ = NULLC ;
  740. DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: Command:%ws",fname));
  741. //
  742. // If only fname type in get path string
  743. //
  744. if (!pcloc) {
  745. tmps02 = GetEnvVar(PathStr) ;
  746. }
  747. DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: PATH:%ws",tmps02));
  748. DoDot = NeedCurrentDirectoryForExePath(loc);
  749. //
  750. // tmps02 is PATH environment variable
  751. // compute enough for PATH environment plus file default path
  752. //
  753. tokpath = mkstr( ((DoDot ? 2 + mystrlen(tmps01) : 0) + mystrlen(tmps02) + 2)*sizeof(TCHAR)) ;
  754. if ( ! tokpath ) {
  755. DosErr = ERROR_NOT_ENOUGH_MEMORY;
  756. return( SFE_FAIL ) ;
  757. }
  758. if (DoDot) {
  759. //
  760. // Copy default path
  761. //
  762. mystrcat(tokpath,TEXT("\""));
  763. mystrcat(tokpath,tmps01) ;
  764. mystrcat(tokpath,TEXT("\""));
  765. }
  766. //
  767. // If only name type in get also delim and path string
  768. //
  769. if (!pcloc) {
  770. if (DoDot) {
  771. mystrcat(tokpath, TEXT(";")) ;
  772. }
  773. mystrcat(tokpath,tmps02) ;
  774. }
  775. //
  776. // Shift left string at ';;'
  777. //
  778. tokshrink(tokpath);
  779. tokpath = TokStr(tokpath, TEXT(";"), TS_WSPACE) ;
  780. cName = mystrlen(fname) + 1 ; // file spec. length
  781. //
  782. // Build up the list of extensions we are going to search
  783. // for. If extensions are enabled, get the list from the PATHEXT
  784. // variable, otherwise use the hard coded default.
  785. //
  786. extPath = NULL;
  787. if (fEnableExtensions)
  788. extPath = GetEnvVar(PathExtStr);
  789. if (extPath == NULL)
  790. extPath = PathExtDefaultStr;
  791. tokshrink(extPath);
  792. extPath = TokStr(extPath, TEXT(";"), TS_WSPACE) ;
  793. //
  794. // Everything is now set up. Var tokpath contains a sequential series
  795. // of asciz strings terminated by an extra null. If the user specified
  796. // a drive or partial path, it contains only that one converted to full
  797. // root-based form. If the user typed only a filename (pcloc = 0) it
  798. // begins with the current directory and contains each directory that
  799. // was contained in the PATH variable. This loop will search each of
  800. // the tokpath elements once for each possible executable extention.
  801. // Note that 'i' is used as a constant to test for excessive string
  802. // length prior to performing the string copies.
  803. //
  804. for ( ; ; ) {
  805. //
  806. // Length of current path
  807. //
  808. tplen = mystrlen(tokpath) ;
  809. mystrcpy( tokpath, StripQuotes( tokpath ) );
  810. tokpathlen = mystrlen(tokpath);
  811. if (*lastc(tokpath) != PathChar) {
  812. addpchar = TRUE;
  813. tplen++;
  814. tokpathlen++;
  815. } else {
  816. addpchar = FALSE;
  817. }
  818. /* path + name too long */
  819. //
  820. // Check if path + name is too long
  821. //
  822. if (*tokpath && (tokpathlen + cName) > MAX_PATH) {
  823. tokpath += addpchar ? tplen : tplen+1; // get next path
  824. continue;
  825. }
  826. //
  827. // If no more paths to search return descriptive error
  828. //
  829. if (*(tokpath) == NULLC) {
  830. if (pcloc) {
  831. if (DosErr == 0 || DosErr == ERROR_FILE_NOT_FOUND)
  832. DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
  833. } else { /* return generic message */
  834. DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
  835. }
  836. return(SFE_NOTFND) ;
  837. }
  838. //
  839. // Install this path and setup for next one
  840. //
  841. mystrcpy(loc, tokpath) ;
  842. tokpath += addpchar ? tplen : tplen+1;
  843. if (addpchar) {
  844. mystrcat(loc, pcstr) ;
  845. }
  846. mystrcat(loc, fname) ;
  847. mystrcpy(loc, StripQuotes(loc) );
  848. dotloc = mystrlen(loc) ;
  849. DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: PATH:%ws",loc));
  850. //
  851. // Check drive in each path to insure it is valid before searching
  852. //
  853. if (*(loc+1) == COLON) {
  854. if (!IsValidDrv(*loc))
  855. continue ;
  856. };
  857. //
  858. // If fname has ext & ext > "." look for given filename
  859. // this says that all executable files must have an extension
  860. //
  861. j = mystrrchr( fname, DOT );
  862. if ( j && j[1] ) {
  863. //
  864. // If access was denied and the user included a path,
  865. // then say we found it. This handles the case where
  866. // we don't have permission to do the findfirst and so we
  867. // can't see the binary, but it actually exists -- if we
  868. // have execute permission, CreateProcess will work
  869. // just fine.
  870. //
  871. if (exists_ex(loc,TRUE) || (pcloc && (DosErr == ERROR_ACCESS_DENIED))) {
  872. //
  873. // Recompute j as exists_ex trims trailing spaces
  874. //
  875. j = mystrrchr( loc, DOT );
  876. if (j != NULL) {
  877. if ( !_tcsicmp(j,CmdExt) ) {
  878. return(SFE_ISBAT) ;
  879. } else if ( !_tcsicmp(j,BatExt) ) {
  880. return(SFE_ISBAT) ;
  881. } else {
  882. return(SFE_ISEXECOM) ;
  883. }
  884. }
  885. }
  886. if ((DosErr != ERROR_FILE_NOT_FOUND) && DosErr)
  887. continue; // Try next path
  888. }
  889. if (mystrchr( fname, STAR ) || mystrchr( fname, QMARK ) ) {
  890. DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
  891. return(SFE_NOTFND);
  892. }
  893. //
  894. // Search for each type of extension
  895. //
  896. extPathWrk = extPath;
  897. if (DoFind(loc, dotloc, TEXT(".*"), FALSE)) // Found anything?
  898. while (*extPathWrk) {
  899. //
  900. // if name + path + ext is less then max path length
  901. //
  902. if ( (cName + tokpathlen + mystrlen(extPathWrk)) <= MAX_PATH) {
  903. //
  904. // See if this extension is a match.
  905. //
  906. if (DoFind(loc, dotloc, extPathWrk, TRUE)) {
  907. if (!_tcsicmp(extPathWrk, BatExt) || !_tcsicmp(extPathWrk, CmdExt))
  908. return(SFE_ISBAT) ; // found .bat or .cmd
  909. else
  910. return(SFE_ISEXECOM) ; // found executable
  911. } else {
  912. //
  913. // Any kind of error other than file not found, bail from
  914. // search and try next element in path
  915. //
  916. if ((DosErr != ERROR_FILE_NOT_FOUND) && DosErr)
  917. break;
  918. }
  919. }
  920. //
  921. // Not this extension, try next.
  922. while (*extPathWrk++)
  923. ;
  924. }
  925. //
  926. // If we get here, then no match with list of extensions.
  927. // If no wierd errors, deal with NUll extension case.
  928. //
  929. if (DosErr == NO_ERROR || DosErr == ERROR_FILE_NOT_FOUND) {
  930. if (DoFind(loc, dotloc, TEXT("\0"), TRUE)) {
  931. if (GetBinaryType(loc,&BinaryType) &&
  932. BinaryType == SCS_POSIX_BINARY) { // Found .
  933. return(SFE_ISEXECOM) ;
  934. }
  935. }
  936. }
  937. } // end for
  938. return(SFE_NOTFND);
  939. }
  940. /*** DoFind - does indiviual findfirsts during searching
  941. *
  942. * Purpose:
  943. * Add the extension to loc and do the find first for
  944. * SearchForExecutable().
  945. *
  946. * DoFind(TCHAR *loc, int dotloc, TCHAR *ext)
  947. *
  948. * Args:
  949. * loc - the string in which the location of the command is to be placed
  950. * dotloc - the location loc at which the extension is to be appended
  951. * ext - the extension to append to loc
  952. *
  953. * Returns:
  954. * 1 if the file is found.
  955. * 0 if the file isn't found.
  956. *
  957. */
  958. int DoFind(loc, dotloc, ext, metas)
  959. TCHAR *loc ;
  960. int dotloc ;
  961. TCHAR *ext ;
  962. BOOL metas;
  963. {
  964. *(loc+dotloc) = NULLC ;
  965. mystrcat(loc, ext) ;
  966. DEBUG((DBENV, DBENVSCAN, "DoFind: exists_ex(%ws)",loc));
  967. return(exists_ex(loc,metas)) ; /*@@4*/
  968. }
  969. /*** ExecError - handles exec errors
  970. *
  971. * Purpose:
  972. * Print the exec error message corresponding to the error number in the
  973. * global variable DosErr.
  974. * @@ lots of error codes added.
  975. * ExecError()
  976. *
  977. */
  978. void ExecError( onb )
  979. TCHAR *onb;
  980. {
  981. unsigned int errmsg;
  982. unsigned int count;
  983. count = ONEARG;
  984. switch (DosErr) {
  985. case ERROR_BAD_DEVICE:
  986. errmsg = MSG_DIR_BAD_COMMAND_OR_FILE;
  987. count = NOARGS;
  988. break;
  989. case ERROR_LOCK_VIOLATION:
  990. errmsg = ERROR_SHARING_VIOLATION;
  991. break ;
  992. case ERROR_NO_PROC_SLOTS:
  993. errmsg = ERROR_NO_PROC_SLOTS;
  994. count = NOARGS;
  995. break ;
  996. case ERROR_NOT_DOS_DISK:
  997. errmsg = ERROR_NOT_DOS_DISK;
  998. break ;
  999. case ERROR_NOT_ENOUGH_MEMORY:
  1000. errmsg = ERROR_NOT_ENOUGH_MEMORY;
  1001. count = NOARGS;
  1002. break ;
  1003. case ERROR_PATH_NOT_FOUND:
  1004. errmsg = MSG_CMD_FILE_NOT_FOUND;
  1005. break ;
  1006. case ERROR_FILE_NOT_FOUND:
  1007. errmsg = MSG_CMD_FILE_NOT_FOUND;
  1008. break ;
  1009. case ERROR_ACCESS_DENIED:
  1010. errmsg = ERROR_ACCESS_DENIED;
  1011. break ;
  1012. case ERROR_EXE_MACHINE_TYPE_MISMATCH:
  1013. errmsg = ERROR_EXE_MACHINE_TYPE_MISMATCH;
  1014. break;
  1015. case ERROR_DRIVE_LOCKED:
  1016. errmsg = ERROR_DRIVE_LOCKED;
  1017. break ;
  1018. case ERROR_INVALID_STARTING_CODESEG:
  1019. errmsg = ERROR_INVALID_STARTING_CODESEG;
  1020. break ;
  1021. case ERROR_INVALID_STACKSEG:
  1022. errmsg = ERROR_INVALID_STACKSEG;
  1023. break ;
  1024. case ERROR_INVALID_MODULETYPE:
  1025. errmsg = ERROR_INVALID_MODULETYPE;
  1026. break ;
  1027. case ERROR_INVALID_EXE_SIGNATURE:
  1028. errmsg = ERROR_INVALID_EXE_SIGNATURE;
  1029. break ;
  1030. case ERROR_EXE_MARKED_INVALID:
  1031. errmsg = ERROR_EXE_MARKED_INVALID;
  1032. break ;
  1033. case ERROR_BAD_EXE_FORMAT:
  1034. errmsg = ERROR_BAD_EXE_FORMAT;
  1035. break ;
  1036. case ERROR_INVALID_MINALLOCSIZE:
  1037. errmsg = ERROR_INVALID_MINALLOCSIZE;
  1038. break ;
  1039. case ERROR_SHARING_VIOLATION:
  1040. errmsg = ERROR_SHARING_VIOLATION;
  1041. break ;
  1042. case ERROR_BAD_ENVIRONMENT:
  1043. errmsg = ERROR_INFLOOP_IN_RELOC_CHAIN;
  1044. count = NOARGS;
  1045. break ;
  1046. case ERROR_INVALID_ORDINAL:
  1047. errmsg = ERROR_INVALID_ORDINAL;
  1048. break ;
  1049. case ERROR_CHILD_NOT_COMPLETE:
  1050. errmsg = ERROR_CHILD_NOT_COMPLETE;
  1051. break ;
  1052. case ERROR_DIRECTORY:
  1053. errmsg = MSG_BAD_CURDIR;
  1054. count = NOARGS;
  1055. break;
  1056. case ERROR_NOT_ENOUGH_QUOTA:
  1057. errmsg = ERROR_NOT_ENOUGH_QUOTA;
  1058. count = NOARGS;
  1059. break;
  1060. case MSG_REAL_MODE_ONLY:
  1061. errmsg = MSG_REAL_MODE_ONLY;
  1062. count = NOARGS;
  1063. break ;
  1064. default:
  1065. // printf( "Exec failed code %x\n", DosErr );
  1066. count = NOARGS;
  1067. errmsg = MSG_EXEC_FAILURE ; /* M031 */
  1068. }
  1069. LastRetCode = errmsg;
  1070. PutStdErr(errmsg, count, onb );
  1071. }
  1072. /*
  1073. * tokshrink @@4
  1074. *
  1075. * remove duplicate ';' in a path
  1076. */
  1077. void tokshrink( tokpath )
  1078. TCHAR *tokpath;
  1079. {
  1080. int i, j;
  1081. i = 0;
  1082. do {
  1083. if ( tokpath[i] == QUOTE ) {
  1084. do {
  1085. i++;
  1086. } while ( tokpath[i] && tokpath[i] != QUOTE );
  1087. }
  1088. if ( tokpath[i] && tokpath[i] != TEXT(';') ) {
  1089. i++;
  1090. }
  1091. if ( tokpath[i] == TEXT(';') ) {
  1092. j = i;
  1093. while ( tokpath[j+1] == TEXT(';') ) {
  1094. j++;
  1095. }
  1096. if ( j > i ) {
  1097. mystrcpy( &tokpath[i], &tokpath[j] );
  1098. }
  1099. i++;
  1100. }
  1101. } while ( tokpath[i] );
  1102. }
  1103. /*** eAssoc - execute an Assoc command
  1104. *
  1105. * Purpose:
  1106. * To set/modify the file associations stored in the registry under the
  1107. * HKEY_LOCAL_MACHINE\Software\Classes key
  1108. *
  1109. * int eAssoc(struct cmdnode *n)
  1110. *
  1111. * Args:
  1112. * n - the parse tree node containing the set command
  1113. *
  1114. * Returns:
  1115. * If setting and the command is syntactically correct, whatever SetAssoc()
  1116. * returns. Otherwise, FAILURE.
  1117. *
  1118. * If displaying, SUCCESS is always returned.
  1119. *
  1120. */
  1121. int eAssoc(n)
  1122. struct cmdnode *n ;
  1123. {
  1124. if (glBatType != CMD_TYPE) {
  1125. // if set command is executed from .bat file OR entered at command prompt
  1126. return( SetLastRetCodeIfError(AssocWork( n )));
  1127. }
  1128. else {
  1129. return( LastRetCode = AssocWork( n ) );
  1130. }
  1131. }
  1132. int AssocWork(n)
  1133. struct cmdnode *n ;
  1134. {
  1135. HKEY hKeyClasses;
  1136. TCHAR *tas ; /* Tokenized argument string */
  1137. TCHAR *wptr ; /* Work pointer */
  1138. int i ; /* Work variable */
  1139. int rc ;
  1140. rc = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes"), &hKeyClasses);
  1141. if (rc) {
  1142. return rc;
  1143. }
  1144. tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
  1145. if (!*tas)
  1146. rc = DisplayAssoc(hKeyClasses, NULL) ;
  1147. else {
  1148. for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
  1149. ;
  1150. /* If too many parameters were given, the second parameter */
  1151. /* wasn't an equal sign, or they didn't specify a string */
  1152. /* return an error message. */
  1153. if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
  1154. !mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
  1155. if (i==1) {
  1156. rc =DisplayAssoc(hKeyClasses, tas);
  1157. } else {
  1158. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  1159. rc = FAILURE ;
  1160. }
  1161. } else {
  1162. rc = SetAssoc(hKeyClasses, tas, wptr+2) ;
  1163. }
  1164. } ;
  1165. RegCloseKey(hKeyClasses) ;
  1166. return rc;
  1167. }
  1168. /*** DisplayAssoc - display a specific file association or all
  1169. *
  1170. * Purpose:
  1171. * To display a specific file association or all
  1172. *
  1173. * int DisplayAssoc( hKeyClasses, tas )
  1174. *
  1175. * Returns:
  1176. * SUCCESS if all goes well
  1177. * FAILURE if it runs out of memory or cannot lock the env. segment
  1178. */
  1179. int DisplayAssoc(hKeyClasses, tas)
  1180. HKEY hKeyClasses;
  1181. TCHAR *tas;
  1182. {
  1183. int i;
  1184. int rc = SUCCESS;
  1185. TCHAR NameBuffer[MAX_PATH];
  1186. TCHAR ValueBuffer[MAX_PATH];
  1187. TCHAR *vstr ;
  1188. DWORD cb;
  1189. if (tas == NULL) {
  1190. for (i=0 ; rc == SUCCESS ; i++) {
  1191. rc = RegEnumKey( hKeyClasses, i, NameBuffer, sizeof( NameBuffer ) / sizeof( TCHAR ));
  1192. if (rc != SUCCESS) {
  1193. if (rc==ERROR_NO_MORE_ITEMS)
  1194. rc = SUCCESS;
  1195. break;
  1196. } else
  1197. if (NameBuffer[0] == DOT) {
  1198. cb = sizeof(ValueBuffer);
  1199. rc = RegQueryValue(hKeyClasses, NameBuffer, ValueBuffer, &cb);
  1200. if (rc != 0) {
  1201. break;
  1202. }
  1203. #if !DBG
  1204. if (_tcslen( ValueBuffer ) != 0)
  1205. #endif
  1206. rc = cmd_printf(Fmt16, NameBuffer, ValueBuffer);
  1207. }
  1208. if (CtrlCSeen) {
  1209. return(FAILURE);
  1210. }
  1211. }
  1212. }
  1213. else {
  1214. tas = EatWS(tas, NULL);
  1215. if ((vstr = mystrrchr(tas, ' ')) != NULL)
  1216. *vstr = NULLC;
  1217. cb = sizeof(ValueBuffer);
  1218. rc = RegQueryValue(hKeyClasses, tas, ValueBuffer, &cb);
  1219. if (rc == 0)
  1220. rc = cmd_printf(Fmt16, tas, ValueBuffer);
  1221. else
  1222. PutStdErr(MSG_ASSOC_NOT_FOUND, ONEARG, tas);
  1223. }
  1224. return(rc);
  1225. }
  1226. /*** SetAssoc - controls adding/changing a file association
  1227. *
  1228. * Purpose:
  1229. * Add/replace a file association
  1230. *
  1231. * int SetAssoc(HKEY hKeyClasses, TCHAR *fileext, TCHAR *filetype)
  1232. *
  1233. * Args:
  1234. * hKeyClasses - handle to HKEY_LOCAL_MACHINE\Software\Classes key
  1235. * fileext - file extension string to associate
  1236. * filetype - file type associate
  1237. *
  1238. * Returns:
  1239. * SUCCESS if the association could be added/replaced.
  1240. * FAILURE otherwise.
  1241. *
  1242. */
  1243. int SetAssoc(hKeyClasses, fileext, filetype)
  1244. HKEY hKeyClasses;
  1245. TCHAR *fileext ;
  1246. TCHAR *filetype ;
  1247. {
  1248. int rc;
  1249. int i;
  1250. DWORD cb;
  1251. //
  1252. // Nothing was specified. We are to delete the key
  1253. //
  1254. if (filetype==NULL || *filetype==NULLC) {
  1255. rc = RegDeleteKey(hKeyClasses, fileext);
  1256. //
  1257. // The key may have subkeys. We only delete the default
  1258. // value, by opening the key and deleting the value
  1259. // with a zero-length name
  1260. //
  1261. if (rc != 0) {
  1262. HKEY hKeyValue;
  1263. rc = RegOpenKey( hKeyClasses, fileext, &hKeyValue );
  1264. if (rc != 0) {
  1265. //
  1266. // If there was no key to begin with, suppress the
  1267. // error
  1268. //
  1269. if (rc != ERROR_FILE_NOT_FOUND) {
  1270. PutStdErr( rc, NOARGS );
  1271. }
  1272. } else {
  1273. rc = RegDeleteValue( hKeyValue, TEXT( "" ) );
  1274. if (rc != 0) {
  1275. PutStdErr( rc, NOARGS );
  1276. }
  1277. RegCloseKey( hKeyValue );
  1278. }
  1279. }
  1280. }
  1281. else {
  1282. //
  1283. // Set the value for the key
  1284. //
  1285. rc = RegSetValue(hKeyClasses, fileext, REG_SZ, filetype, _tcslen(filetype));
  1286. if (rc == 0) {
  1287. cmd_printf( Fmt16, fileext, filetype );
  1288. }
  1289. else
  1290. PutStdErr(MSG_ERR_PROC_ARG, ONEARG, fileext);
  1291. }
  1292. //
  1293. // If the value was changed/deleted successfully, notify all
  1294. // apps about this
  1295. //
  1296. if (rc == 0) {
  1297. try {
  1298. SHChangeNotify( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL );
  1299. } except (DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
  1300. }
  1301. }
  1302. return rc;
  1303. }
  1304. /*** eFType - execute an FType command
  1305. *
  1306. * Purpose:
  1307. * To set/modify the file types stored in the registry under the
  1308. * HKEY_LOCAL_MACHINE\Software\Classes key
  1309. *
  1310. * int eFType(struct cmdnode *n)
  1311. *
  1312. * Args:
  1313. * n - the parse tree node containing the set command
  1314. *
  1315. * Returns:
  1316. * If setting and the command is syntactically correct, whatever SetFType()
  1317. * returns. Otherwise, FAILURE.
  1318. *
  1319. * If displaying, SUCCESS is always returned.
  1320. *
  1321. */
  1322. int eFType(n)
  1323. struct cmdnode *n ;
  1324. {
  1325. if (glBatType != CMD_TYPE) {
  1326. // if set command is executed from .bat file OR entered at command prompt
  1327. return( SetLastRetCodeIfError(FTypeWork( n )));
  1328. }
  1329. else {
  1330. return( LastRetCode = FTypeWork( n ) );
  1331. }
  1332. }
  1333. int FTypeWork(n)
  1334. struct cmdnode *n ;
  1335. {
  1336. HKEY hKeyClasses;
  1337. TCHAR *tas ; /* Tokenized argument string */
  1338. TCHAR *wptr ; /* Work pointer */
  1339. int i ; /* Work variable */
  1340. int rc ;
  1341. rc = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes"), &hKeyClasses);
  1342. if (rc) {
  1343. return rc;
  1344. }
  1345. tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
  1346. if (!*tas)
  1347. rc = DisplayFType(hKeyClasses, NULL) ;
  1348. else {
  1349. for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
  1350. ;
  1351. /* If too many parameters were given, the second parameter */
  1352. /* wasn't an equal sign, or they didn't specify a string */
  1353. /* return an error message. */
  1354. if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
  1355. !mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
  1356. if (i==1) {
  1357. rc =DisplayFType(hKeyClasses, tas);
  1358. } else {
  1359. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  1360. rc = FAILURE ;
  1361. }
  1362. } else {
  1363. rc = SetFType(hKeyClasses, tas, wptr+2) ;
  1364. }
  1365. } ;
  1366. RegCloseKey(hKeyClasses) ;
  1367. return rc;
  1368. }
  1369. /*** DisplayFType - display a specific file type or all
  1370. *
  1371. * Purpose:
  1372. * To display a specific file type or all
  1373. *
  1374. * int DisplayFType( hKeyClasses, tas )
  1375. *
  1376. * Returns:
  1377. * SUCCESS if all goes well
  1378. * FAILURE if it runs out of memory or cannot lock the env. segment
  1379. */
  1380. int DisplayFType(hKeyClasses, tas)
  1381. HKEY hKeyClasses;
  1382. TCHAR *tas;
  1383. {
  1384. int i;
  1385. int rc;
  1386. HKEY hKeyOpenCmd;
  1387. TCHAR NameBuffer[MAX_PATH];
  1388. TCHAR ValueBuffer[MAX_PATH];
  1389. TCHAR *vstr ;
  1390. DWORD cb, j, Type;
  1391. if (tas == NULL) {
  1392. for (i = 0;;i++) {
  1393. rc = RegEnumKey( hKeyClasses, i, NameBuffer, sizeof( NameBuffer ) / sizeof( TCHAR ));
  1394. if (rc != 0) {
  1395. if (rc==ERROR_NO_MORE_ITEMS)
  1396. rc = SUCCESS;
  1397. break;
  1398. } else if (NameBuffer[0] != DOT) {
  1399. j = _tcslen( NameBuffer );
  1400. if (j + _tcslen( ShellOpenCommandString ) + 1 <= MAX_PATH) {
  1401. _tcscat(NameBuffer, ShellOpenCommandString );
  1402. _tcscpy(ValueBuffer,TEXT("*** no open command defined ***"));
  1403. rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
  1404. if (!rc) {
  1405. NameBuffer[j] = TEXT('\0');
  1406. cb = sizeof(ValueBuffer);
  1407. rc = RegQueryValueEx(hKeyOpenCmd, TEXT(""), NULL, &Type, (LPBYTE)ValueBuffer, &cb);
  1408. RegCloseKey(hKeyOpenCmd);
  1409. }
  1410. if (!rc) {
  1411. cmd_printf(Fmt16, NameBuffer, ValueBuffer);
  1412. }
  1413. }
  1414. }
  1415. if (CtrlCSeen) {
  1416. return(FAILURE);
  1417. }
  1418. }
  1419. } else {
  1420. if (*tas == DOT) {
  1421. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
  1422. return ERROR_INVALID_NAME;
  1423. }
  1424. tas = EatWS(tas, NULL);
  1425. if ((vstr = mystrrchr(tas, ' ')) != NULL)
  1426. *vstr = NULLC;
  1427. if (_tcslen( tas ) + _tcslen( ShellOpenCommandString ) + 1 > MAX_PATH) {
  1428. PutStdErr(MSG_FTYPE_TOO_LONG, ONEARG, tas);
  1429. return ERROR_INVALID_NAME;
  1430. }
  1431. _tcscpy( NameBuffer, tas );
  1432. _tcscat( NameBuffer, ShellOpenCommandString );
  1433. rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
  1434. if (rc) {
  1435. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
  1436. return rc;
  1437. }
  1438. cb = sizeof(ValueBuffer);
  1439. rc = RegQueryValueEx(hKeyOpenCmd, TEXT(""), NULL, &Type, (LPBYTE)ValueBuffer, &cb);
  1440. if (rc == 0) {
  1441. ValueBuffer[ (cb / sizeof( TCHAR )) - 1 ];
  1442. cmd_printf(Fmt16, tas, ValueBuffer);
  1443. } else
  1444. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
  1445. RegCloseKey( hKeyOpenCmd );
  1446. }
  1447. return(rc);
  1448. }
  1449. /*** SetFType - controls adding/changing the open command associated with a file type
  1450. *
  1451. * Purpose:
  1452. * Add/replace an open command string associated with a file type
  1453. *
  1454. * int SetFType(HKEY hKeyOpenCmd, TCHAR *filetype TCHAR *opencmd)
  1455. *
  1456. * Args:
  1457. * hKeyClasses - handle to HKEY_LOCAL_MACHINE\Software\Classes
  1458. * filetype - file type name
  1459. * opencmd - open command string
  1460. *
  1461. * Returns:
  1462. * SUCCESS if the file type could be added/replaced.
  1463. * FAILURE otherwise.
  1464. *
  1465. */
  1466. int SetFType(hKeyClasses, filetype, opencmd)
  1467. HKEY hKeyClasses;
  1468. TCHAR *filetype ;
  1469. TCHAR *opencmd ;
  1470. {
  1471. HKEY hKeyOpenCmd;
  1472. TCHAR NameBuffer[MAX_PATH];
  1473. TCHAR c, *s;
  1474. DWORD Disposition;
  1475. int rc;
  1476. int i;
  1477. DWORD cb;
  1478. if (_tcslen( filetype ) + _tcslen( ShellOpenCommandString ) + 1 > MAX_PATH) {
  1479. PutStdErr( MSG_FTYPE_TOO_LONG, NOARGS );
  1480. return FAILURE;
  1481. }
  1482. _tcscpy( NameBuffer, filetype );
  1483. _tcscat( NameBuffer, ShellOpenCommandString );
  1484. rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
  1485. if (rc) {
  1486. if (opencmd==NULL || *opencmd==NULLC) {
  1487. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
  1488. return rc;
  1489. }
  1490. s = NameBuffer;
  1491. while (TRUE) {
  1492. while (*s && *s != TEXT('\\')) {
  1493. s += 1;
  1494. }
  1495. c = *s;
  1496. *s = TEXT('\0');
  1497. rc = RegCreateKeyEx(hKeyClasses,
  1498. NameBuffer,
  1499. 0,
  1500. NULL,
  1501. 0,
  1502. (REGSAM)MAXIMUM_ALLOWED,
  1503. NULL,
  1504. &hKeyOpenCmd,
  1505. &Disposition
  1506. );
  1507. if (rc) {
  1508. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
  1509. return rc;
  1510. }
  1511. if (c == TEXT('\0')) {
  1512. break;
  1513. }
  1514. *s++ = c;
  1515. RegCloseKey(hKeyOpenCmd);
  1516. }
  1517. }
  1518. if (opencmd==NULL || *opencmd==NULLC) {
  1519. rc = RegDeleteKey(hKeyOpenCmd, NULL);
  1520. if (rc != 0)
  1521. PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
  1522. }
  1523. else {
  1524. rc = RegSetValueEx(hKeyOpenCmd, TEXT(""), 0, REG_EXPAND_SZ, (LPBYTE)opencmd, (_tcslen(opencmd)+1)*sizeof(TCHAR));
  1525. if (rc == 0)
  1526. cmd_printf(Fmt16, filetype, opencmd);
  1527. else
  1528. PutStdErr(MSG_ERR_PROC_ARG, ONEARG, filetype);
  1529. }
  1530. RegCloseKey(hKeyOpenCmd);
  1531. return rc;
  1532. }
  1533. typedef
  1534. NTSTATUS
  1535. (NTAPI *PNTQUERYINFORMATIONPROCESS)(
  1536. IN HANDLE ProcessHandle,
  1537. IN PROCESSINFOCLASS ProcessInformationClass,
  1538. OUT PVOID ProcessInformation,
  1539. IN ULONG ProcessInformationLength,
  1540. OUT PULONG ReturnLength OPTIONAL
  1541. );
  1542. HMODULE hNtDllModule;
  1543. PNTQUERYINFORMATIONPROCESS lpNtQueryInformationProcess;
  1544. WORD
  1545. GetProcessSubsystemType(
  1546. HANDLE hProcess
  1547. )
  1548. {
  1549. PIMAGE_NT_HEADERS NtHeader;
  1550. PPEB PebAddress;
  1551. PEB Peb;
  1552. SIZE_T SizeOfPeb;
  1553. NTSTATUS Status;
  1554. PROCESS_BASIC_INFORMATION ProcessInfo;
  1555. BOOL b;
  1556. PVOID ImageBaseAddress;
  1557. LONG e_lfanew;
  1558. WORD Subsystem;
  1559. Subsystem = IMAGE_SUBSYSTEM_UNKNOWN;
  1560. if (hNtDllModule == NULL) {
  1561. hNtDllModule = LoadLibrary( TEXT("NTDLL.DLL") );
  1562. if (hNtDllModule != NULL) {
  1563. lpNtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)
  1564. GetProcAddress( hNtDllModule,
  1565. "NtQueryInformationProcess"
  1566. );
  1567. }
  1568. else {
  1569. hNtDllModule = INVALID_HANDLE_VALUE;
  1570. }
  1571. }
  1572. if (lpNtQueryInformationProcess != NULL) {
  1573. //
  1574. // Get the Peb address
  1575. //
  1576. Status = (*lpNtQueryInformationProcess)( hProcess,
  1577. ProcessBasicInformation,
  1578. &ProcessInfo,
  1579. sizeof( ProcessInfo ),
  1580. NULL
  1581. );
  1582. if (NT_SUCCESS( Status )) {
  1583. PebAddress = ProcessInfo.PebBaseAddress;
  1584. //
  1585. // Read the subsystem type from the Peb
  1586. //
  1587. if (ReadProcessMemory( hProcess,
  1588. PebAddress,
  1589. &Peb,
  1590. sizeof( Peb ),
  1591. &SizeOfPeb
  1592. )
  1593. ) {
  1594. //
  1595. // See if we are running on a system that has the image subsystem
  1596. // type captured in the PEB. If so use it. Otherwise go the slow
  1597. // way and try to get it from the image header.
  1598. //
  1599. if (SizeOfPeb >= FIELD_OFFSET( PEB, ImageSubsystem ) &&
  1600. ((UINT_PTR)Peb.ProcessHeaps - (UINT_PTR)PebAddress) > FIELD_OFFSET( PEB, ImageSubsystem )
  1601. ) {
  1602. Subsystem = (WORD)Peb.ImageSubsystem;
  1603. }
  1604. else {
  1605. //
  1606. // read e_lfanew from imageheader
  1607. //
  1608. if (ReadProcessMemory( hProcess,
  1609. &((PIMAGE_DOS_HEADER)Peb.ImageBaseAddress)->e_lfanew,
  1610. &e_lfanew,
  1611. sizeof( e_lfanew ),
  1612. NULL
  1613. )
  1614. ) {
  1615. //
  1616. // Read subsystem version info
  1617. //
  1618. NtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)Peb.ImageBaseAddress + e_lfanew);
  1619. if (ReadProcessMemory( hProcess,
  1620. &NtHeader->OptionalHeader.Subsystem,
  1621. &Subsystem,
  1622. sizeof( Subsystem ),
  1623. NULL
  1624. )
  1625. ) {
  1626. }
  1627. }
  1628. }
  1629. }
  1630. }
  1631. }
  1632. return Subsystem;
  1633. }