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.

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