sh.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. sh.c
  5. Abstract:
  6. This module implements the shell application.
  7. Author:
  8. Evan Green 5-Jun-2013
  9. Environment:
  10. User Mode
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "sh.h"
  16. #include "shparse.h"
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "../swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define SHELL_PS1_INTERACTIVE_DEFAULT "\\w\\$ "
  28. #define SH_USAGE \
  29. "usage: sh [-abCefhimnuvx] [-o option] command_file [argument...]\n" \
  30. " sh -c [-abCefhimnuvx] command_string [command_name [argument...]]" \
  31. "\n" \
  32. " sh -s [-abCefhimnuvx] [argument]\n" \
  33. "The sh utility provides a basic POSIX shell. Basic forms are:\n" \
  34. " sh ... command_file - Read shell commands from the given file.\n" \
  35. " sh ... -c command_string - Interpret the given command string in the " \
  36. "shell.\n" \
  37. " sh ... -s - Read commands from standard in. This is the default.\n\n" \
  38. "Options can be turned on by specifying -abCefhimnuvx or -o <option>.\n" \
  39. "Options can be turned off by using +abCefhimnuvx or +o <option>.\n" \
  40. "Options are:\n" \
  41. " -a (allexport) -- Set the export attribute to any variable \n" \
  42. " assignment of shell-wide scope.\n" \
  43. " -b (notify) -- Enables asynchronous background notifications.\n" \
  44. " -C -- Do not clobber existing files with the > redirection operator.\n" \
  45. " -d -- Debug mode. Prints the lexing and parsing of shell commands.\n" \
  46. " -e (errexit) -- Exit the shell if any command returns a non-zero \n" \
  47. " status.\n" \
  48. " -f (noglob) -- Disables pathname expansions.\n" \
  49. " -h -- Cache utility paths invoked by functions.\n" \
  50. " -i -- Treat the shell as interactive.\n" \
  51. " -m -- Run all jobs in their own process groups.\n" \
  52. " -n (noexec) -- Read but do not execute commands (ignored if \n" \
  53. " interactive).\n" \
  54. " -o -- Sets a long-form option (clear on +o).\n" \
  55. " -u (nounset) -- Print a message to standard error whenever an \n" \
  56. " attempt is made to expand an unset variable and immediately \n" \
  57. " exit (except if interactive).\n" \
  58. " -v (verbose) -- Write all input to standard out as it is read.\n" \
  59. " -x (xtrace) -- Write a trace of each command after it expands but \n" \
  60. " before it executes.\n" \
  61. " --help -- Show this help text and exit.\n" \
  62. " --version -- Show the application version information and exit.\n\n" \
  63. //
  64. // ------------------------------------------------------ Data Type Definitions
  65. //
  66. typedef struct _SHELL_OPTION_STRING {
  67. PSTR String;
  68. CHAR Character;
  69. ULONG Option;
  70. } SHELL_OPTION_STRING, *PSHELL_OPTION_STRING;
  71. //
  72. // ----------------------------------------------- Internal Function Prototypes
  73. //
  74. VOID
  75. ShRunEnvVariable (
  76. PSHELL Shell
  77. );
  78. //
  79. // -------------------------------------------------------------------- Globals
  80. //
  81. SHELL_OPTION_STRING ShOptionStrings[] = {
  82. {"allexport", 'a', SHELL_OPTION_EXPORT_ALL},
  83. {"errexit", 'e', SHELL_OPTION_EXIT_ON_FAILURE},
  84. {"ignoreeof", 0, SHELL_OPTION_IGNORE_EOF},
  85. {"monitor", 'm', SHELL_OPTION_RUN_JOBS_IN_SEPARATE_PROCESS_GROUP},
  86. {"noclobber", 'C', SHELL_OPTION_NO_CLOBBER},
  87. {"noglob", 'f', SHELL_OPTION_NO_PATHNAME_EXPANSION},
  88. {"noexec", 'n', SHELL_OPTION_NO_EXECUTE},
  89. {"nolog", 0, SHELL_OPTION_NO_COMMAND_HISTORY},
  90. {"notify", 'b', SHELL_OPTION_ASYNCHRONOUS_JOB_NOTIFICATION},
  91. {"nounset", 'u', SHELL_OPTION_EXIT_ON_UNSET_VARIABLE},
  92. {"verbose", 'v', SHELL_OPTION_DISPLAY_INPUT},
  93. {"interactive", 'i', SHELL_INTERACTIVE_OPTIONS},
  94. {"xtrace", 'x', SHELL_OPTION_TRACE_COMMAND},
  95. {"debug", 'd', SHELL_OPTION_DEBUG},
  96. {"stdin", 's', SHELL_OPTION_READ_FROM_STDIN},
  97. {NULL, 'h', SHELL_OPTION_LOCATE_UTILITIES_IN_DECLARATION},
  98. };
  99. //
  100. // ------------------------------------------------------------------ Functions
  101. //
  102. INT
  103. ShMain (
  104. INT ArgumentCount,
  105. CHAR **Arguments
  106. )
  107. /*++
  108. Routine Description:
  109. This routine implements the main entry point for the shell app.
  110. Arguments:
  111. ArgumentCount - Supplies the number of arguments on the command line.
  112. Arguments - Supplies an array of pointers to strings representing the
  113. arguments.
  114. Return Value:
  115. 0 on success.
  116. Non-zero on failure.
  117. --*/
  118. {
  119. PSTR Argument;
  120. ULONG ArgumentIndex;
  121. BOOL ArgumentIsInput;
  122. UINTN ArgumentSize;
  123. INT InputDescriptor;
  124. INT InputDescriptorHigh;
  125. BOOL Result;
  126. INT ReturnValue;
  127. BOOL Set;
  128. PSHELL Shell;
  129. INT StandardErrorCopy;
  130. InputDescriptor = -1;
  131. InputDescriptorHigh = -1;
  132. srand(time(NULL));
  133. ReturnValue = ENOMEM;
  134. Shell = ShCreateShell(NULL, 0);
  135. if (Shell == NULL) {
  136. PRINT_ERROR("Error: Unable to allocate shell.\n");
  137. goto MainEnd;
  138. }
  139. StandardErrorCopy = ShDup(Shell, STDERR_FILENO, FALSE);
  140. if (StandardErrorCopy >= 0) {
  141. Shell->NonStandardError = fdopen(StandardErrorCopy, "w");
  142. if (Shell->NonStandardError == NULL) {
  143. ShClose(Shell, StandardErrorCopy);
  144. }
  145. }
  146. //
  147. // Loop through all the options.
  148. //
  149. ArgumentIsInput = FALSE;
  150. for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) {
  151. Argument = Arguments[ArgumentIndex];
  152. //
  153. // First look out for the longform options.
  154. //
  155. if ((strcmp(Argument, "-o") == 0) || (strcmp(Argument, "+o") == 0)) {
  156. Set = TRUE;
  157. if (Argument[0] == '+') {
  158. Set = FALSE;
  159. }
  160. if (ArgumentIndex == ArgumentCount - 1) {
  161. Argument = "";
  162. ArgumentSize = 1;
  163. } else {
  164. ArgumentIndex += 1;
  165. Argument = Arguments[ArgumentIndex];
  166. ArgumentSize = strlen(Arguments[ArgumentIndex + 1]) + 1;
  167. }
  168. Result = ShSetOptions(Shell,
  169. Argument,
  170. ArgumentSize,
  171. TRUE,
  172. Set,
  173. NULL);
  174. if (Result == FALSE) {
  175. PRINT_ERROR("Error: Unknown option %s.\n",
  176. Arguments[ArgumentIndex + 1]);
  177. ReturnValue = EINVAL;
  178. goto MainEnd;
  179. }
  180. continue;
  181. }
  182. //
  183. // Stop processing for --.
  184. //
  185. if (strcmp(Argument, "--") == 0) {
  186. break;
  187. }
  188. //
  189. // Look for help or version if they're the only arguments.
  190. //
  191. if (ArgumentCount == 2) {
  192. if (strcmp(Argument, "--help") == 0) {
  193. printf(SH_USAGE);
  194. Result = 1;
  195. goto MainEnd;
  196. } else if (strcmp(Argument, "--version") == 0) {
  197. SwPrintVersion(SH_VERSION_MAJOR, SH_VERSION_MINOR);
  198. Result = 1;
  199. goto MainEnd;
  200. }
  201. }
  202. if ((Argument[0] == '-') || (Argument[0] == '+')) {
  203. Result = ShSetOptions(Shell,
  204. Argument,
  205. strlen(Argument) + 1,
  206. FALSE,
  207. FALSE,
  208. &ArgumentIsInput);
  209. if (Result == FALSE) {
  210. ReturnValue = EINVAL;
  211. goto MainEnd;
  212. }
  213. //
  214. // This is either a command file, command string, or argument, so stop
  215. // processing options.
  216. //
  217. } else {
  218. break;
  219. }
  220. }
  221. //
  222. // If the next command is the input, set that up directly.
  223. //
  224. if (ArgumentIsInput != FALSE) {
  225. if (ArgumentIndex == ArgumentCount) {
  226. PRINT_ERROR("Error: -c requires an argument.");
  227. ReturnValue = EINVAL;
  228. goto MainEnd;
  229. }
  230. Argument = Arguments[ArgumentIndex];
  231. Shell->Lexer.InputBufferSize = strlen(Argument) + 1;
  232. Shell->Lexer.InputBuffer = SwStringDuplicate(
  233. Argument,
  234. Shell->Lexer.InputBufferSize);
  235. if (Shell->Lexer.InputBuffer == NULL) {
  236. goto MainEnd;
  237. }
  238. Shell->Lexer.InputBufferCapacity = Shell->Lexer.InputBufferSize;
  239. ArgumentIndex += 1;
  240. //
  241. // The following argument if provided is the command name.
  242. //
  243. if (ArgumentIndex < ArgumentCount) {
  244. Argument = Arguments[ArgumentIndex];
  245. ArgumentIndex += 1;
  246. //
  247. // If no argument zero was provided, then use this argument zero.
  248. //
  249. } else {
  250. Argument = Arguments[0];
  251. }
  252. Shell->CommandNameSize = strlen(Argument) + 1;
  253. Shell->CommandName = SwStringDuplicate(Argument,
  254. Shell->CommandNameSize);
  255. if (Shell->CommandName == NULL) {
  256. goto MainEnd;
  257. }
  258. //
  259. // If not reading from standard in, then the next argument is a script. If
  260. // there is no argument, then it's standard in.
  261. //
  262. } else if ((Shell->Options & SHELL_OPTION_READ_FROM_STDIN) == 0) {
  263. if (ArgumentIndex == ArgumentCount) {
  264. Shell->Lexer.InputFile = NULL;
  265. Shell->Options |= SHELL_OPTION_READ_FROM_STDIN;
  266. } else {
  267. Argument = Arguments[ArgumentIndex];
  268. InputDescriptor = open(Argument, O_RDONLY | O_BINARY);
  269. if (InputDescriptor < 0) {
  270. SwPrintError(errno, Argument, "Unable to open script");
  271. ReturnValue = SHELL_ERROR_OPEN;
  272. goto MainEnd;
  273. }
  274. if (InputDescriptor >= SHELL_MINIMUM_FILE_DESCRIPTOR) {
  275. InputDescriptorHigh = InputDescriptor;
  276. } else {
  277. InputDescriptorHigh = ShDup(Shell, InputDescriptor, FALSE);
  278. if (InputDescriptorHigh < 0) {
  279. SwPrintError(errno, Argument, "Unable to dup");
  280. ReturnValue = SHELL_ERROR_OPEN;
  281. goto MainEnd;
  282. }
  283. assert(InputDescriptorHigh >= SHELL_MINIMUM_FILE_DESCRIPTOR);
  284. close(InputDescriptor);
  285. }
  286. InputDescriptor = -1;
  287. Shell->Lexer.InputFile = fdopen(InputDescriptorHigh, "rb");
  288. if (Shell->Lexer.InputFile == NULL) {
  289. PRINT_ERROR("Error: Unable to open script %s.\n", Argument);
  290. ReturnValue = SHELL_ERROR_OPEN;
  291. goto MainEnd;
  292. }
  293. InputDescriptorHigh = -1;
  294. //
  295. // Also set the first argument to the name of this script.
  296. //
  297. Shell->CommandNameSize = strlen(Argument) + 1;
  298. Shell->CommandName = SwStringDuplicate(Argument,
  299. Shell->CommandNameSize);
  300. if (Shell->CommandName == NULL) {
  301. goto MainEnd;
  302. }
  303. ArgumentIndex += 1;
  304. }
  305. }
  306. //
  307. // If the command name has not yet been set, assign it to the first
  308. // argument.
  309. //
  310. if (Shell->CommandName == NULL) {
  311. Shell->CommandNameSize = strlen(Arguments[0]) + 1;
  312. Shell->CommandName = SwStringDuplicate(Arguments[0],
  313. Shell->CommandNameSize);
  314. if (Shell->CommandName == NULL) {
  315. goto MainEnd;
  316. }
  317. }
  318. //
  319. // Set up any remaining arguments as the positional arguments.
  320. //
  321. if (ArgumentIndex != ArgumentCount) {
  322. Result = ShCreateArgumentList(Arguments + ArgumentIndex,
  323. ArgumentCount - ArgumentIndex,
  324. &(Shell->ArgumentList));
  325. if (Result == FALSE) {
  326. goto MainEnd;
  327. }
  328. }
  329. //
  330. // If the input is a terminal, then mark this shell as interactive.
  331. //
  332. assert(InputDescriptor == -1);
  333. if ((Shell->Options & SHELL_OPTION_READ_FROM_STDIN) != 0) {
  334. InputDescriptor = STDIN_FILENO;
  335. } else if (Shell->Lexer.InputFile != NULL) {
  336. InputDescriptor = fileno(Shell->Lexer.InputFile);
  337. }
  338. if ((InputDescriptor != -1) && (isatty(InputDescriptor) != 0)) {
  339. Shell->Options |= SHELL_INTERACTIVE_OPTIONS;
  340. }
  341. InputDescriptor = -1;
  342. //
  343. // Give them something a little snazzier than a plain jane dollar sign
  344. // if it's interactive. Failure here isn't critical.
  345. //
  346. if ((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) {
  347. ShSetVariable(Shell,
  348. SHELL_PS1,
  349. sizeof(SHELL_PS1),
  350. SHELL_PS1_INTERACTIVE_DEFAULT,
  351. sizeof(SHELL_PS1_INTERACTIVE_DEFAULT));
  352. }
  353. ShInitializeSignals(Shell);
  354. //
  355. // Run the contents of the ENV environment variable if appropriate.
  356. //
  357. ShRunEnvVariable(Shell);
  358. //
  359. // Here we go, run that shell!
  360. //
  361. Result = ShExecute(Shell, &ReturnValue);
  362. if (Result == FALSE) {
  363. ReturnValue = errno;
  364. if (ReturnValue == 0) {
  365. ReturnValue = EINVAL;
  366. }
  367. }
  368. Shell->Exited = TRUE;
  369. ShRunAtExitSignal(Shell);
  370. ShRestoreOriginalSignalDispositions();
  371. MainEnd:
  372. if (InputDescriptor >= 0) {
  373. close(InputDescriptor);
  374. }
  375. if (InputDescriptorHigh >= 0) {
  376. close(InputDescriptor);
  377. }
  378. if (Shell != NULL) {
  379. ShDestroyShell(Shell);
  380. }
  381. return ReturnValue;
  382. }
  383. BOOL
  384. ShSetOptions (
  385. PSHELL Shell,
  386. PSTR String,
  387. UINTN StringSize,
  388. BOOL LongForm,
  389. BOOL Set,
  390. PBOOL HasC
  391. )
  392. /*++
  393. Routine Description:
  394. This routine sets shell behavior options.
  395. Arguments:
  396. Shell - Supplies a pointer to the shell.
  397. String - Supplies a pointer to the string containing the options.
  398. StringSize - Supplies the size of the string in bytes including the null
  399. terminator.
  400. LongForm - Supplies a boolean indicating if this is a long form option,
  401. where the name of the option is spelled out.
  402. Set - Supplies whether or not the longform argument is a set (-o) or clear
  403. (+o) operation. For non longform arguments, this parameter is ignored.
  404. HasC - Supplies an optional boolean indicating if the option string has -c
  405. int it somewhere. If NULL, then -c is not allowed.
  406. Return Value:
  407. TRUE on success.
  408. FALSE if an invalid option was supplied.
  409. --*/
  410. {
  411. CHAR Character;
  412. UINTN Index;
  413. UINTN OptionCount;
  414. ULONG Options;
  415. UINTN StringIndex;
  416. BOOL WriteOptions;
  417. WriteOptions = FALSE;
  418. Options = 0;
  419. OptionCount = sizeof(ShOptionStrings) / sizeof(ShOptionStrings[0]);
  420. if (LongForm != FALSE) {
  421. for (Index = 0; Index < OptionCount; Index += 1) {
  422. if ((ShOptionStrings[Index].String != NULL) &&
  423. (strcasecmp(String, ShOptionStrings[Index].String) == 0)) {
  424. Options |= ShOptionStrings[Index].Option;
  425. break;
  426. }
  427. }
  428. if (Options == 0) {
  429. if (StringSize == 1) {
  430. WriteOptions = TRUE;
  431. } else {
  432. SwPrintError(0, String, "Unrecognized option");
  433. return FALSE;
  434. }
  435. }
  436. } else {
  437. Set = TRUE;
  438. if (String[0] == '+') {
  439. Set = FALSE;
  440. }
  441. for (StringIndex = 1; StringIndex < StringSize; StringIndex += 1) {
  442. Character = String[StringIndex];
  443. if (Character == '\0') {
  444. break;
  445. }
  446. for (Index = 0; Index < OptionCount; Index += 1) {
  447. if ((ShOptionStrings[Index].Character != 0) &&
  448. (ShOptionStrings[Index].Character == Character)) {
  449. Options |= ShOptionStrings[Index].Option;
  450. break;
  451. }
  452. }
  453. if (Index == OptionCount) {
  454. //
  455. // Ignore a -c and notify the caller if the caller might be
  456. // expecting it.
  457. //
  458. if ((Character == 'c') && (HasC != NULL)) {
  459. *HasC = TRUE;
  460. continue;
  461. }
  462. PRINT_ERROR("Error: Invalid option '%c'.\n", Character);
  463. return FALSE;
  464. }
  465. }
  466. }
  467. if (Set != FALSE) {
  468. Shell->Options |= Options;
  469. } else {
  470. Shell->Options &= ~Options;
  471. }
  472. //
  473. // Set or clear the various debug globals.
  474. //
  475. if ((Shell->Options & SHELL_OPTION_DEBUG) != 0) {
  476. ShDebugAlias = TRUE;
  477. ShDebugArithmeticLexer = TRUE;
  478. ShDebugArithmeticParser = TRUE;
  479. ShDebugLexer = TRUE;
  480. ShDebugPrintParseTree = TRUE;
  481. } else {
  482. ShDebugAlias = FALSE;
  483. ShDebugArithmeticLexer = FALSE;
  484. ShDebugArithmeticParser = FALSE;
  485. ShDebugLexer = FALSE;
  486. ShDebugPrintParseTree = FALSE;
  487. }
  488. //
  489. // If asked, write out all the options.
  490. //
  491. if (WriteOptions != FALSE) {
  492. Options = Shell->Options;
  493. for (Index = 0; Index < OptionCount; Index += 1) {
  494. if (ShOptionStrings[Index].String != NULL) {
  495. if ((ShOptionStrings[Index].Option & Options) != 0) {
  496. printf("set -o %s\n", ShOptionStrings[Index].String);
  497. } else {
  498. printf("set +o %s\n", ShOptionStrings[Index].String);
  499. }
  500. }
  501. }
  502. }
  503. return TRUE;
  504. }
  505. //
  506. // --------------------------------------------------------- Internal Functions
  507. //
  508. VOID
  509. ShRunEnvVariable (
  510. PSHELL Shell
  511. )
  512. /*++
  513. Routine Description:
  514. This routine expands and runs the contents of the ENV variable in the
  515. current context if appropriate.
  516. Arguments:
  517. Shell - Supplies a pointer to the shell.
  518. Return Value:
  519. None.
  520. --*/
  521. {
  522. int DescriptorAnywhere;
  523. int EnvironmentDescriptor;
  524. FILE *EnvironmentFile;
  525. PSTR ExpandedScript;
  526. UINTN ExpandedScriptSize;
  527. SHELL_LEXER_STATE OriginalLexer;
  528. ULONG OriginalOptions;
  529. BOOL Result;
  530. INT ReturnValue;
  531. PSTR Value;
  532. UINTN ValueSize;
  533. EnvironmentFile = NULL;
  534. ExpandedScript = NULL;
  535. //
  536. // Ignore the ENV variable if this is not an interactive script or the
  537. // real and effective user IDs differ.
  538. //
  539. if (((Shell->Options & SHELL_OPTION_INTERACTIVE) == 0) ||
  540. (SwGetRealUserId() != SwGetEffectiveUserId())) {
  541. return;
  542. }
  543. //
  544. // If the ENV variable is set, it is expanded and then represents the
  545. // path of a file to execute in the context of this shell.
  546. //
  547. Result = ShGetVariable(Shell,
  548. SHELL_ENV,
  549. sizeof(SHELL_ENV),
  550. &Value,
  551. &ValueSize);
  552. if (Result == FALSE) {
  553. goto RunEnvVariableEnd;
  554. }
  555. Result = ShPerformExpansions(Shell,
  556. Value,
  557. ValueSize,
  558. SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT,
  559. &ExpandedScript,
  560. &ExpandedScriptSize,
  561. NULL,
  562. NULL);
  563. if (Result == FALSE) {
  564. PRINT_ERROR("Warning: Unable to expand ENV.\n");
  565. goto RunEnvVariableEnd;
  566. }
  567. assert(Shell->Lexer.LexerPrimed == FALSE);
  568. assert(Shell->Lexer.InputBufferNextIndex == 0);
  569. assert(Shell->Lexer.UnputCharacterValid == FALSE);
  570. assert(Shell->Lexer.LineNumber == 1);
  571. //
  572. // Open up the env file.
  573. //
  574. DescriptorAnywhere = open(ExpandedScript, O_RDONLY | O_BINARY);
  575. if (DescriptorAnywhere == -1) {
  576. PRINT_ERROR("Warning: Unable to open ENV file %s: %s.\n",
  577. ExpandedScript,
  578. strerror(errno));
  579. goto RunEnvVariableEnd;
  580. }
  581. EnvironmentDescriptor = ShDup(Shell, DescriptorAnywhere, TRUE);
  582. close(DescriptorAnywhere);
  583. if (EnvironmentDescriptor < 0) {
  584. goto RunEnvVariableEnd;
  585. }
  586. EnvironmentFile = fdopen(EnvironmentDescriptor, "rb");
  587. if (EnvironmentFile == NULL) {
  588. close(EnvironmentDescriptor);
  589. goto RunEnvVariableEnd;
  590. }
  591. assert((Shell->Options & SHELL_OPTION_INPUT_BUFFER_ONLY) == 0);
  592. //
  593. // Copy the original lexer state over and reinitialize the lexer
  594. // for this string.
  595. //
  596. memcpy(&OriginalLexer, &(Shell->Lexer), sizeof(SHELL_LEXER_STATE));
  597. Result = ShInitializeLexer(&(Shell->Lexer),
  598. EnvironmentFile,
  599. NULL,
  600. 0);
  601. if (Result == FALSE) {
  602. goto RunEnvVariableEnd;
  603. }
  604. EnvironmentFile = NULL;
  605. //
  606. // Run the env script.
  607. //
  608. OriginalOptions = Shell->Options;
  609. Shell->Options &= ~(SHELL_OPTION_RAW_INPUT |
  610. SHELL_OPTION_PRINT_PROMPTS |
  611. SHELL_OPTION_NO_COMMAND_HISTORY |
  612. SHELL_OPTION_READ_FROM_STDIN);
  613. Result = ShExecute(Shell, &ReturnValue);
  614. Shell->Options = OriginalOptions;
  615. if (Result == FALSE) {
  616. PRINT_ERROR("Warning: Failed to execute ENV script %s.\n",
  617. ExpandedScript);
  618. }
  619. //
  620. // Clean up this lexer (which includes closing the file) and
  621. // restore the original lexer.
  622. //
  623. ShDestroyLexer(&(Shell->Lexer));
  624. memcpy(&(Shell->Lexer), &OriginalLexer, sizeof(SHELL_LEXER_STATE));
  625. Result = TRUE;
  626. RunEnvVariableEnd:
  627. if (EnvironmentFile != NULL) {
  628. fclose(EnvironmentFile);
  629. }
  630. if (ExpandedScript != NULL) {
  631. free(ExpandedScript);
  632. }
  633. return;
  634. }