1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- exec.c
- Abstract:
- This module handles execution of commands for the shell.
- Author:
- Evan Green 6-Jun-2013
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "sh.h"
- #include "shparse.h"
- #include "../swiss.h"
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "../swlib.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // Define the file creation mask for new files created by I/O redirection.
- //
- #define SHELL_FILE_CREATION_MASK 0664
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- BOOL
- ShExecuteNode (
- PSHELL Shell,
- PSHELL_NODE Node
- );
- BOOL
- ShExecuteAsynchronousNode (
- PSHELL Shell,
- PSHELL_NODE Node
- );
- BOOL
- ShExecuteList (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteAndOr (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecutePipeline (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteSimpleCommand (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteFunctionDefinition (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteFunctionInvocation (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutingNode,
- PSHELL_NODE Function,
- PSTR *Arguments,
- ULONG ArgumentCount
- );
- BOOL
- ShExecuteIf (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteFor (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteCase (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteWhileOrUntil (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- BOOL
- ShExecuteSubshellGroup (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- VOID
- ShExitOnError (
- PSHELL Shell
- );
- BOOL
- ShApplyRedirections (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Set this variable if swiss command should be recognized even before
- // searching the path.
- //
- BOOL ShUseSwissBuiltins = TRUE;
- //
- // Define the quoted at arguments string.
- //
- CHAR ShQuotedAtArgumentsString[] =
- {SHELL_CONTROL_QUOTE, '$', '@', SHELL_CONTROL_QUOTE, '\0'};
- //
- // ------------------------------------------------------------------ Functions
- //
- BOOL
- ShExecute (
- PSHELL Shell,
- PINT ReturnValue
- )
- /*++
- Routine Description:
- This routine executes commands from the input of the shell.
- Arguments:
- Shell - Supplies a pointer to the shell whose input should be read and
- executed.
- ReturnValue - Supplies a pointer where the return value of the shell will
- be returned.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Command;
- BOOL Result;
- Result = FALSE;
- ShPrintPrompt(Shell, 1);
- while (Shell->Exited == FALSE) {
- ShCheckForSignals(Shell);
- Result = ShParse(Shell, &Command);
- if (Result == FALSE) {
- if ((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) {
- ShPrintPrompt(Shell, 1);
- Shell->Lexer.LexerPrimed = FALSE;
- continue;
- }
- break;
- }
- if (Command == NULL) {
- break;
- }
- if ((Shell->Options & SHELL_OPTION_NO_EXECUTE) == 0) {
- Result = ShExecuteNode(Shell, Command);
- }
- ShReleaseNode(Command);
- if (Result == FALSE) {
- if ((Shell->Options & SHELL_OPTION_INTERACTIVE) == 0) {
- break;
- }
- }
- }
- *ReturnValue = Shell->LastReturnValue;
- return Result;
- }
- VOID
- ShRestoreRedirections (
- PSHELL Shell,
- PLIST_ENTRY ActiveRedirectList
- )
- /*++
- Routine Description:
- This routine restores all active redirections back to their previous state.
- Arguments:
- Shell - Supplies a pointer to the shell.
- ActiveRedirectList - Supplies a pointer to the active redirect list.
- Return Value:
- None.
- --*/
- {
- PSHELL_ACTIVE_REDIRECT ActiveRedirect;
- INT ReplacedDescriptor;
- while (LIST_EMPTY(ActiveRedirectList) == FALSE) {
- //
- // Loop backwards so that if the same descriptor is redirected multiple
- // times then its value gets popped back to the original.
- //
- ActiveRedirect = LIST_VALUE(ActiveRedirectList->Previous,
- SHELL_ACTIVE_REDIRECT,
- ListEntry);
- LIST_REMOVE(&(ActiveRedirect->ListEntry));
- if (ActiveRedirect->OriginalDescriptor != -1) {
- ReplacedDescriptor = ShDup2(Shell,
- ActiveRedirect->OriginalDescriptor,
- ActiveRedirect->FileNumber);
- if (ReplacedDescriptor < 0) {
- PRINT_ERROR("Failed to restore file number %d.\n",
- ActiveRedirect->FileNumber);
- }
- ShClose(Shell, ActiveRedirect->OriginalDescriptor);
- //
- // If there was no original descriptor there, close whatever there is
- // now to restore it to former non-glory.
- //
- } else {
- ShClose(Shell, ActiveRedirect->FileNumber);
- }
- if (ActiveRedirect->ChildProcessId > 0) {
- SwWaitPid(ActiveRedirect->ChildProcessId, 0, NULL);
- }
- free(ActiveRedirect);
- }
- return;
- }
- VOID
- ShSetTerminalMode (
- PSHELL Shell,
- BOOL Raw
- )
- /*++
- Routine Description:
- This routine potentially sets the terminal input mode one way or another.
- If the interactive flag is not set, this does nothing.
- Arguments:
- Shell - Supplies a pointer to the shell. If the interactive flag is not set
- in this shell, then nothing happens.
- Raw - Supplies a boolean indicating whether to set it in raw mode (TRUE)
- or it's previous original mode (FALSE).
- Return Value:
- None.
- --*/
- {
- if ((Shell->Options & SHELL_OPTION_INTERACTIVE) == 0) {
- return;
- }
- if (Raw != FALSE) {
- SwSetRawInputMode(&ShBackspaceCharacter, &ShKillLineCharacter);
- } else {
- SwRestoreInputMode();
- }
- return;
- }
- INT
- ShRunCommand (
- PSHELL Shell,
- PSTR Command,
- PSTR *Arguments,
- INT ArgumentCount,
- INT Asynchronous,
- PINT ReturnValue
- )
- /*++
- Routine Description:
- This routine is called to run a basic command for the shell.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Command - Supplies a pointer to the command name to run.
- Arguments - Supplies a pointer to an array of command argument strings.
- This includes the first argument, the command name.
- ArgumentCount - Supplies the number of arguments on the command line.
- Asynchronous - Supplies 0 if the shell should wait until the command is
- finished, or 1 if the function should return immediately with a
- return value of 0.
- ReturnValue - Supplies a pointer where the return value from the executed
- program will be returned.
- Return Value:
- 0 if the executable was successfully launched.
- Non-zero if there was trouble launching the executable.
- --*/
- {
- pid_t Child;
- PSTR FullCommandPath;
- ULONG FullCommandPathSize;
- BOOL Result;
- INT Status;
- PSWISS_COMMAND_ENTRY SwissCommand;
- id_t UserId;
- FullCommandPath = NULL;
- *ReturnValue = -1;
- Child = -1;
- //
- // If enabled, try the builtin commands.
- //
- if (ShUseSwissBuiltins != FALSE) {
- SwissCommand = SwissFindCommand(Arguments[0]);
- //
- // If the command is setuid and the environment is currently not setuid,
- // pretend like the command wasn't found.
- //
- if ((SwissCommand != NULL) &&
- ((SwissCommand->Flags & SWISS_APP_SETUID_OK) != 0)) {
- UserId = SwGetEffectiveUserId();
- if ((UserId != 0) && (UserId == SwGetRealUserId())) {
- SwissCommand = NULL;
- }
- }
- if (SwissCommand != NULL) {
- if (SwForkSupported != 0) {
- Child = SwFork();
- if (Child < 0) {
- PRINT_ERROR("sh: Failed to fork: %s\n", strerror(errno));
- Status = -1;
- goto RunCommandEnd;
- } else if (Child == 0) {
- ShRestoreOriginalSignalDispositions();
- SwissRunCommand(SwissCommand,
- Arguments,
- ArgumentCount,
- FALSE,
- TRUE,
- ReturnValue);
- exit(*ReturnValue);
- //
- // In the parent, jump down to wait for the child.
- //
- } else {
- Status = 0;
- goto RunCommandEnd;
- }
- //
- // If fork is not supported (Windows), just execute the command in
- // a separate process.
- //
- } else {
- fflush(NULL);
- Result = SwissRunCommand(SwissCommand,
- Arguments,
- ArgumentCount,
- TRUE,
- !Asynchronous,
- ReturnValue);
- if (Result != FALSE) {
- Status = 0;
- ShOsConvertExitStatus(ReturnValue);
- goto RunCommandEnd;
- }
- }
- }
- }
- *ReturnValue = 0;
- Result = ShLocateCommand(Shell,
- Arguments[0],
- strlen(Arguments[0]) + 1,
- TRUE,
- &FullCommandPath,
- &FullCommandPathSize,
- ReturnValue);
- if (Result == FALSE) {
- Status = -1;
- goto RunCommandEnd;
- }
- if (*ReturnValue != 0) {
- if (*ReturnValue == SHELL_ERROR_OPEN) {
- PRINT_ERROR("sh: %s: Command not found.\n", Arguments[0]);
- } else if (*ReturnValue == SHELL_ERROR_EXECUTE) {
- PRINT_ERROR("sh: %s: Permission denied.\n", Arguments[0]);
- }
- Status = 0;
- goto RunCommandEnd;
- }
- if (SwForkSupported != 0) {
- Child = SwFork();
- if (Child < 0) {
- PRINT_ERROR("sh: Failed to fork: %s\n", strerror(errno));
- Status = -1;
- goto RunCommandEnd;
- } else if (Child == 0) {
- ShRestoreOriginalSignalDispositions();
- SwExec(FullCommandPath, Arguments, ArgumentCount);
- exit(errno);
- //
- // In the parent, jump down to wait for the child.
- //
- } else {
- Status = 0;
- goto RunCommandEnd;
- }
- } else {
- fflush(NULL);
- Status = SwRunCommand(FullCommandPath,
- Arguments,
- ArgumentCount,
- Asynchronous,
- ReturnValue);
- ShOsConvertExitStatus(ReturnValue);
- }
- RunCommandEnd:
- //
- // Wait for the child if there is one.
- //
- if (Child > 0) {
- if (Asynchronous != 0) {
- *ReturnValue = 0;
- } else {
- Status = SwWaitPid(Child, 0, ReturnValue);
- if (Status != Child) {
- PRINT_ERROR("sh: Failed to wait for child %d\n", Child);
- Status = -1;
- } else {
- ShOsConvertExitStatus(ReturnValue);
- }
- }
- }
- if (*ReturnValue > SHELL_EXIT_SIGNALED) {
- if ((Shell->Options & SHELL_OPTION_INTERACTIVE) != 0) {
- printf("%s terminated by signal %d: %s\n",
- Arguments[0],
- *ReturnValue - SHELL_EXIT_SIGNALED,
- strsignal(*ReturnValue - SHELL_EXIT_SIGNALED));
- }
- //
- // If the command exited normally, save any terminal changes it may have
- // made.
- //
- } else {
- SwSaveTerminalMode();
- }
- if ((FullCommandPath != NULL) && (FullCommandPath != Arguments[0])) {
- free(FullCommandPath);
- }
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- BOOL
- ShExecuteNode (
- PSHELL Shell,
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine executes a generic shell node.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- Node - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_EXECUTION_NODE ExecutionNode;
- ULONG OriginalLineNumber;
- BOOL Result;
- if ((Node->RunInBackground != FALSE) && (SwForkSupported != 0)) {
- return ShExecuteAsynchronousNode(Shell, Node);
- }
- //
- // Create an execution node and push it on the stack.
- //
- ExecutionNode = malloc(sizeof(SHELL_EXECUTION_NODE));
- if (ExecutionNode == NULL) {
- return FALSE;
- }
- INITIALIZE_LIST_HEAD(&(ExecutionNode->VariableList));
- INITIALIZE_LIST_HEAD(&(ExecutionNode->ArgumentList));
- INITIALIZE_LIST_HEAD(&(ExecutionNode->ActiveRedirectList));
- ExecutionNode->Node = Node;
- ExecutionNode->Flags = 0;
- ExecutionNode->ReturnValue = 0;
- INSERT_AFTER(&(ExecutionNode->ListEntry), &(Shell->ExecutionStack));
- OriginalLineNumber = Shell->ExecutingLineNumber;
- Shell->ExecutingLineNumber = Node->LineNumber;
- Result = ShApplyRedirections(Shell, ExecutionNode);
- if (Result == FALSE) {
- goto ExecuteNodeEnd;
- }
- Result = FALSE;
- switch (Node->Type) {
- case ShellNodeList:
- case ShellNodeTerm:
- case ShellNodeBraceGroup:
- Result = ShExecuteList(Shell, ExecutionNode);
- break;
- case ShellNodeAndOr:
- Result = ShExecuteAndOr(Shell, ExecutionNode);
- break;
- case ShellNodePipeline:
- Result = ShExecutePipeline(Shell, ExecutionNode);
- break;
- case ShellNodeSimpleCommand:
- Result = ShExecuteSimpleCommand(Shell, ExecutionNode);
- break;
- case ShellNodeFunction:
- Result = ShExecuteFunctionDefinition(Shell, ExecutionNode);
- break;
- case ShellNodeIf:
- Result = ShExecuteIf(Shell, ExecutionNode);
- break;
- case ShellNodeFor:
- Result = ShExecuteFor(Shell, ExecutionNode);
- break;
- case ShellNodeCase:
- Result = ShExecuteCase(Shell, ExecutionNode);
- break;
- case ShellNodeWhile:
- case ShellNodeUntil:
- Result = ShExecuteWhileOrUntil(Shell, ExecutionNode);
- break;
- case ShellNodeSubshell:
- Result = ShExecuteSubshellGroup(Shell, ExecutionNode);
- break;
- default:
- assert(FALSE);
- Result = FALSE;
- }
- ExecuteNodeEnd:
- //
- // Remove the node from the stack if not already done.
- //
- if (ExecutionNode->ListEntry.Next != NULL) {
- LIST_REMOVE(&(ExecutionNode->ListEntry));
- }
- ShDestroyArgumentList(&(ExecutionNode->ArgumentList));
- ShDestroyVariableList(&(ExecutionNode->VariableList));
- ShRestoreRedirections(Shell, &(ExecutionNode->ActiveRedirectList));
- free(ExecutionNode);
- Shell->ExecutingLineNumber = OriginalLineNumber;
- Shell->LastReturnValue = Shell->ReturnValue;
- return Result;
- }
- BOOL
- ShExecuteAsynchronousNode (
- PSHELL Shell,
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine executes a shell node asynchronously.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- Node - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- pid_t Process;
- BOOL Result;
- assert((Node->RunInBackground != FALSE) && (SwForkSupported != 0));
- //
- // Attempt to fork the current process, which is easiest and most in line
- // with what the shell would like to do.
- //
- Process = SwFork();
- //
- // If this is the child, set the node to be synchronous, run the node, and
- // exit straight away. Altering the node here doesn't change the memory of
- // the parent process remember. See how easy that was?
- //
- if (Process == 0) {
- if (Shell->PostForkCloseDescriptor != -1) {
- close(Shell->PostForkCloseDescriptor);
- Shell->PostForkCloseDescriptor = -1;
- }
- Node->RunInBackground = FALSE;
- ShExecuteNode(Shell, Node);
- exit(Shell->LastReturnValue);
- //
- // If this is the parent process, then work is finished here. Even easier!
- //
- } else if (Process != -1) {
- Result = TRUE;
- goto ExecuteAsynchronousNodeEnd;
- //
- // Fork failed.
- //
- } else {
- Result = FALSE;
- goto ExecuteAsynchronousNodeEnd;
- }
- Result = TRUE;
- ExecuteAsynchronousNodeEnd:
- if (Result == FALSE) {
- Shell->ReturnValue = 1;
- } else {
- Shell->ReturnValue = 0;
- }
- return Result;
- }
- BOOL
- ShExecuteList (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a list node.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Child;
- PLIST_ENTRY CurrentEntry;
- PSHELL_NODE Node;
- BOOL Result;
- Node = ExecutionNode->Node;
- assert((Node->Type == ShellNodeList) || (Node->Type == ShellNodeTerm) ||
- (Node->Type == ShellNodeBraceGroup));
- CurrentEntry = Node->Children.Next;
- while (CurrentEntry != &(Node->Children)) {
- Child = LIST_VALUE(CurrentEntry, SHELL_NODE, SiblingListEntry);
- CurrentEntry = CurrentEntry->Next;
- Result = ShExecuteNode(Shell, Child);
- if (Result == FALSE) {
- return FALSE;
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- //
- // Break out if the execution node was removed from the stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- break;
- }
- }
- return TRUE;
- }
- BOOL
- ShExecuteAndOr (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a logical And-Or (&&/||) node.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Child;
- PLIST_ENTRY CurrentEntry;
- BOOL Execute;
- PSHELL_NODE Node;
- PSHELL_NODE Previous;
- BOOL Result;
- Node = ExecutionNode->Node;
- assert(Node->Type == ShellNodeAndOr);
- Previous = NULL;
- CurrentEntry = Node->Children.Next;
- while (CurrentEntry != &(Node->Children)) {
- Child = LIST_VALUE(CurrentEntry, SHELL_NODE, SiblingListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // The first node always executes. If the previous node was an AND,
- // then don't execute this node if the previous node failed. If the
- // previous node was an OR, then don't execute this node if the
- // previous node succeeded.
- //
- Execute = TRUE;
- if (Previous != NULL) {
- if (Previous->AndOr == TOKEN_DOUBLE_AND) {
- if (Shell->LastReturnValue != 0) {
- Execute = FALSE;
- }
- } else if (Previous->AndOr == TOKEN_DOUBLE_OR) {
- if (Shell->LastReturnValue == 0) {
- Execute = FALSE;
- }
- }
- }
- if (Execute != FALSE) {
- Result = ShExecuteNode(Shell, Child);
- if (Result == FALSE) {
- return FALSE;
- }
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- //
- // Break out if the execution node was removed from the stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- break;
- }
- Previous = Child;
- }
- return TRUE;
- }
- BOOL
- ShExecutePipeline (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a pipeline.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Child;
- PLIST_ENTRY CurrentEntry;
- INT NextPipe[2];
- PSHELL_NODE Node;
- INT OriginalStandardIn;
- INT OriginalStandardOut;
- INT PreviousPipeRead;
- BOOL Result;
- NextPipe[0] = -1;
- NextPipe[1] = -1;
- Node = ExecutionNode->Node;
- OriginalStandardIn = -1;
- OriginalStandardOut = -1;
- PreviousPipeRead = -1;
- Result = TRUE;
- assert(Node->Type == ShellNodePipeline);
- CurrentEntry = Node->Children.Next;
- while (CurrentEntry != &(Node->Children)) {
- Child = LIST_VALUE(CurrentEntry, SHELL_NODE, SiblingListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // If this is not the last node, create a new pipe and wire standard out
- // up to that pipe. If it is the last node, leave standard out wired up
- // the way it is.
- //
- if (Child->SiblingListEntry.Next != &(Node->Children)) {
- Result = ShCreatePipe(NextPipe);
- if (Result == FALSE) {
- return FALSE;
- }
- OriginalStandardOut = ShDup(Shell, STDOUT_FILENO, FALSE);
- ShDup2(Shell, NextPipe[1], STDOUT_FILENO);
- ShClose(Shell, NextPipe[1]);
- NextPipe[1] = -1;
- }
- //
- // If this is not the first node, wire up standard input to the
- // previous pipe's read end.
- //
- if (Child->SiblingListEntry.Previous != &(Node->Children)) {
- OriginalStandardIn = ShDup(Shell, STDIN_FILENO, FALSE);
- assert(PreviousPipeRead != -1);
- ShDup2(Shell, PreviousPipeRead, STDIN_FILENO);
- ShClose(Shell, PreviousPipeRead);
- PreviousPipeRead = -1;
- }
- //
- // Save the previous pipe's read entry. Make it a non-inheritable
- // handle so that when the next process closes standard in, that's the
- // last open handle.
- //
- if (NextPipe[0] != -1) {
- PreviousPipeRead = ShDup(Shell, NextPipe[0], FALSE);
- ShClose(Shell, NextPipe[0]);
- NextPipe[0] = -1;
- Shell->PostForkCloseDescriptor = PreviousPipeRead;
- }
- Result = ShExecuteNode(Shell, Child);
- //
- // Restore standard in and standard out if they were changed.
- //
- if (OriginalStandardIn != -1) {
- ShDup2(Shell, OriginalStandardIn, STDIN_FILENO);
- ShClose(Shell, OriginalStandardIn);
- OriginalStandardIn = -1;
- }
- if (OriginalStandardOut != -1) {
- ShDup2(Shell, OriginalStandardOut, STDOUT_FILENO);
- ShClose(Shell, OriginalStandardOut);
- OriginalStandardOut = -1;
- }
- if (PreviousPipeRead != -1) {
- assert((Shell->PostForkCloseDescriptor == PreviousPipeRead) ||
- (Shell->PostForkCloseDescriptor == -1));
- Shell->PostForkCloseDescriptor = -1;
- }
- //
- // If executing the command failed, stop now.
- //
- if (Result == FALSE) {
- goto ExecutePipelineEnd;
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- //
- // Break out if the execution node was removed from the stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- break;
- }
- }
- assert(Shell->ReturnValue == Shell->LastReturnValue);
- if ((Shell->Exited == FALSE) && (Node->U.Pipeline.Bang != FALSE)) {
- Shell->ReturnValue = !Shell->LastReturnValue;
- }
- ExecutePipelineEnd:
- if (OriginalStandardIn != -1) {
- ShDup2(Shell, OriginalStandardIn, STDIN_FILENO);
- ShClose(Shell, OriginalStandardIn);
- OriginalStandardIn = -1;
- }
- if (OriginalStandardOut != -1) {
- ShDup2(Shell, OriginalStandardOut, STDOUT_FILENO);
- ShClose(Shell, OriginalStandardOut);
- OriginalStandardOut = -1;
- }
- if (NextPipe[0] != -1) {
- ShClose(Shell, NextPipe[0]);
- }
- if (NextPipe[1] != -1) {
- ShClose(Shell, NextPipe[1]);
- }
- //
- // Check for signals to reap any child processes that were created.
- //
- ShCheckForSignals(Shell);
- return Result;
- }
- BOOL
- ShExecuteSimpleCommand (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a simple command. The function makes it sound easier
- than it is.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- ULONG ArgumentCount;
- ULONG ArgumentIndex;
- PSTR *Arguments;
- BOOL Asynchronous;
- PSHELL_BUILTIN_COMMAND BuiltinCommand;
- PSTR ExpandedArguments;
- UINTN ExpandedArgumentsSize;
- PSHELL_FUNCTION Function;
- PSHELL_EXECUTION_NODE LatestExecutionNode;
- PSHELL_NODE Node;
- BOOL Result;
- INT ReturnValue;
- PSHELL_NODE_SIMPLE_COMMAND SimpleCommand;
- ArgumentCount = 0;
- Arguments = NULL;
- ExpandedArguments = NULL;
- Node = ExecutionNode->Node;
- assert(Node->Type == ShellNodeSimpleCommand);
- SimpleCommand = &(Node->U.SimpleCommand);
- if ((Shell->Options & SHELL_OPTION_TRACE_COMMAND) != 0) {
- ShPrintPrompt(Shell, 4);
- }
- Shell->ReturnValue = 0;
- Result = ShExecuteVariableAssignments(Shell, ExecutionNode);
- if (Result == FALSE) {
- goto ExecuteSimpleCommandEnd;
- }
- if (SimpleCommand->Arguments != NULL) {
- //
- // Perform expansions, field splitting, and quote removal.
- //
- Result = ShPerformExpansions(Shell,
- SimpleCommand->Arguments,
- SimpleCommand->ArgumentsSize,
- 0,
- &ExpandedArguments,
- &ExpandedArgumentsSize,
- &Arguments,
- &ArgumentCount);
- if (Result == FALSE) {
- goto ExecuteSimpleCommandEnd;
- }
- }
- //
- // If tracing is enabled, print the tracing prompt and then the command
- //
- if ((Shell->Options & SHELL_OPTION_TRACE_COMMAND) != 0) {
- for (ArgumentIndex = 0;
- ArgumentIndex < ArgumentCount;
- ArgumentIndex += 1) {
- ShPrintTrace(Shell, "%s ", Arguments[ArgumentIndex]);
- }
- ShPrintTrace(Shell, "\n");
- }
- if (SimpleCommand->Arguments == NULL) {
- Result = TRUE;
- goto ExecuteSimpleCommandEnd;
- }
- //
- // If the command is empty, don't do much.
- //
- if ((ArgumentCount == 0) || (strlen(Arguments[0]) == 0)) {
- Result = TRUE;
- goto ExecuteSimpleCommandEnd;
- }
- Asynchronous = FALSE;
- if (Node->RunInBackground != FALSE) {
- Asynchronous = TRUE;
- }
- //
- // Check to see if this is a builtin command, and run it if it is.
- //
- BuiltinCommand = ShIsBuiltinCommand(Arguments[0]);
- if (BuiltinCommand != NULL) {
- ReturnValue = ShRunBuiltinCommand(Shell,
- BuiltinCommand,
- ArgumentCount,
- Arguments);
- //
- // Put the return value on the most recent execution node.
- //
- if (!LIST_EMPTY(&(Shell->ExecutionStack))) {
- LatestExecutionNode = LIST_VALUE(Shell->ExecutionStack.Next,
- SHELL_EXECUTION_NODE,
- ListEntry);
- LatestExecutionNode->ReturnValue = ReturnValue;
- }
- } else {
- //
- // Look to see if this is a function, and run that function if so.
- //
- Function = ShGetFunction(Shell, Arguments[0], strlen(Arguments[0]) + 1);
- if (Function != NULL) {
- Result = ShExecuteFunctionInvocation(Shell,
- ExecutionNode,
- Function->Node,
- Arguments + 1,
- ArgumentCount - 1);
- goto ExecuteSimpleCommandEnd;
- }
- Result = TRUE;
- ShRunCommand(Shell,
- Arguments[0],
- Arguments,
- ArgumentCount,
- Asynchronous,
- &ReturnValue);
- }
- Shell->ReturnValue = ReturnValue;
- Result = TRUE;
- ExecuteSimpleCommandEnd:
- //
- // If the simple command failed and exit on errors is set, potentially
- // exit.
- //
- if ((Shell->Options & SHELL_OPTION_EXIT_ON_FAILURE) != 0) {
- ShExitOnError(Shell);
- }
- if (Arguments != NULL) {
- free(Arguments);
- }
- if (ExpandedArguments != NULL) {
- free(ExpandedArguments);
- }
- return Result;
- }
- BOOL
- ShExecuteFunctionDefinition (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a function definition node. The definitions don't
- actually run the function, so this is a no-op.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- BOOL Result;
- //
- // Make the function declaration active in the shell.
- //
- Result = ShDeclareFunction(Shell, ExecutionNode->Node);
- if (Result == FALSE) {
- return FALSE;
- }
- //
- // Function definitions are successful if they were parsed correctly, which
- // it was.
- //
- Shell->ReturnValue = 0;
- return TRUE;
- }
- BOOL
- ShExecuteFunctionInvocation (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutingNode,
- PSHELL_NODE Function,
- PSTR *Arguments,
- ULONG ArgumentCount
- )
- /*++
- Routine Description:
- This routine executes a function.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutingNode - Supplies a pointer to the execution node this function is
- run as. This will temporarily get pointed to the function node.
- Function - Supplies a pointer to the function to execute.
- Arguments - Supplies a pointer to the array of strings containing the
- arguments.
- ArgumentCount - Supplies the number of arguments in the array.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Body;
- PSHELL_NODE OriginalNode;
- BOOL Result;
- assert(ExecutingNode->Node->Type == ShellNodeSimpleCommand);
- assert((ExecutingNode->Flags & SHELL_EXECUTION_BODY) == 0);
- OriginalNode = ExecutingNode->Node;
- ExecutingNode->Node = Function;
- ExecutingNode->Flags |= SHELL_EXECUTION_BODY;
- assert(LIST_EMPTY(&(ExecutingNode->ArgumentList)) != FALSE);
- //
- // Create an argument list out of the incoming arguments.
- //
- Result = ShCreateArgumentList(Arguments,
- ArgumentCount,
- &(ExecutingNode->ArgumentList));
- if (Result == FALSE) {
- goto ExecuteFunctionInvocationEnd;
- }
- //
- // There should only be one thing in the children list, the compound
- // body statement.
- //
- assert((LIST_EMPTY(&(Function->Children)) == FALSE) &&
- (Function->Children.Next->Next == &(Function->Children)));
- Body = LIST_VALUE(Function->Children.Next, SHELL_NODE, SiblingListEntry);
- Result = ShExecuteNode(Shell, Body);
- if (Result == FALSE) {
- goto ExecuteFunctionInvocationEnd;
- }
- ExecuteFunctionInvocationEnd:
- ExecutingNode->Flags &= ~SHELL_EXECUTION_BODY;
- //
- // If the options were made local, restore them now.
- //
- if ((ExecutingNode->Flags & SHELL_EXECUTION_RESTORE_OPTIONS) != 0) {
- Shell->Options = ExecutingNode->SavedOptions;
- }
- ExecutingNode->Node = OriginalNode;
- //
- // Destroy the current argument list, as it's the ones set up for the
- // function.
- //
- ShDestroyArgumentList(&(ExecutingNode->ArgumentList));
- return Result;
- }
- BOOL
- ShExecuteIf (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes an if statement.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Condition;
- INT ConditionReturn;
- PSHELL_NODE FalseStatement;
- PSHELL_NODE Node;
- BOOL Result;
- PSHELL_NODE TrueStatement;
- Node = ExecutionNode->Node;
- assert(Node->Type == ShellNodeIf);
- //
- // Get the condition, true, and maybe the false conditions.
- //
- assert(Node->Children.Next != &(Node->Children));
- Condition = LIST_VALUE(Node->Children.Next, SHELL_NODE, SiblingListEntry);
- assert(Condition->SiblingListEntry.Next != &(Node->Children));
- TrueStatement = LIST_VALUE(Condition->SiblingListEntry.Next,
- SHELL_NODE,
- SiblingListEntry);
- FalseStatement = NULL;
- if (TrueStatement->SiblingListEntry.Next != &(Node->Children)) {
- FalseStatement = LIST_VALUE(TrueStatement->SiblingListEntry.Next,
- SHELL_NODE,
- SiblingListEntry);
- }
- Result = ShExecuteNode(Shell, Condition);
- if (Result == FALSE) {
- return FALSE;
- }
- if (Shell->Exited != FALSE) {
- return TRUE;
- }
- ConditionReturn = Shell->LastReturnValue;
- Shell->ReturnValue = 0;
- //
- // Break out if no longer on the execution stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- return TRUE;
- }
- //
- // Go to the true statement if the return value was zero.
- //
- ExecutionNode->Flags |= SHELL_EXECUTION_BODY;
- if (ConditionReturn == 0) {
- Result = ShExecuteNode(Shell, TrueStatement);
- } else if (FalseStatement != NULL) {
- Result = ShExecuteNode(Shell, FalseStatement);
- }
- ExecutionNode->Flags &= ~SHELL_EXECUTION_BODY;
- return Result;
- }
- BOOL
- ShExecuteFor (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a for loop.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE DoGroup;
- PSHELL_NODE_FOR ForStatement;
- PSHELL_NODE Node;
- BOOL Result;
- PSTR SplitBuffer;
- ULONG WordCount;
- ULONG WordIndex;
- PSTR WordListString;
- UINTN WordListStringSize;
- PSTR *Words;
- Node = ExecutionNode->Node;
- SplitBuffer = NULL;
- WordListString = NULL;
- Words = NULL;
- assert(Node->Type == ShellNodeFor);
- assert(LIST_EMPTY(&(Node->Children)) == FALSE);
- ForStatement = &(Node->U.For);
- DoGroup = LIST_VALUE(Node->Children.Next, SHELL_NODE, SiblingListEntry);
- assert(DoGroup->SiblingListEntry.Next == &(Node->Children));
- //
- // Expand the word list. If there is no word list, use the parameters.
- //
- if (ForStatement->WordListBuffer == NULL) {
- Result = ShPerformExpansions(Shell,
- ShQuotedAtArgumentsString,
- sizeof(ShQuotedAtArgumentsString),
- 0,
- &WordListString,
- &WordListStringSize,
- &Words,
- &WordCount);
- } else {
- Result = ShPerformExpansions(Shell,
- ForStatement->WordListBuffer,
- ForStatement->WordListBufferSize,
- 0,
- &WordListString,
- &WordListStringSize,
- &Words,
- &WordCount);
- }
- if (Result == FALSE) {
- goto ExecuteForEnd;
- }
- //
- // If there are no words anymore, simply end.
- //
- if (WordCount == 0) {
- Shell->ReturnValue = 0;
- Result = TRUE;
- goto ExecuteForEnd;
- }
- //
- // Loop through every word, assign the variable, and execute the do group.
- //
- for (WordIndex = 0; WordIndex < WordCount; WordIndex += 1) {
- Result = ShSetVariable(Shell,
- ForStatement->Name,
- ForStatement->NameSize,
- Words[WordIndex],
- strlen(Words[WordIndex]) + 1);
- if (Result == FALSE) {
- goto ExecuteForEnd;
- }
- Result = ShExecuteNode(Shell, DoGroup);
- if (Result == FALSE) {
- goto ExecuteForEnd;
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- //
- // Stop if this execution node is no longer on the stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- break;
- }
- }
- ExecuteForEnd:
- if (WordListString != NULL) {
- free(WordListString);
- }
- if (SplitBuffer != NULL) {
- free(SplitBuffer);
- }
- if (Words != NULL) {
- free(Words);
- }
- return Result;
- }
- BOOL
- ShExecuteCase (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a list node.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PLIST_ENTRY CurrentPatternEntry;
- PLIST_ENTRY CurrentSetEntry;
- PSTR ExpandedPattern;
- UINTN ExpandedPatternSize;
- PSTR Input;
- UINTN InputSize;
- BOOL Match;
- PSHELL_NODE Node;
- ULONG Options;
- PSHELL_CASE_PATTERN_ENTRY PatternEntry;
- BOOL Result;
- PSHELL_CASE_PATTERN_SET Set;
- Input = NULL;
- Match = FALSE;
- Node = ExecutionNode->Node;
- assert(Node->Type == ShellNodeCase);
- //
- // Get and expand the input.
- //
- Result = ShPerformExpansions(Shell,
- Node->U.Case.Name,
- Node->U.Case.NameSize,
- SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT,
- &Input,
- &InputSize,
- NULL,
- NULL);
- if (Result == FALSE) {
- goto ExecuteCaseEnd;
- }
- if (LIST_EMPTY(&(Node->U.Case.PatternList)) != FALSE) {
- Result = TRUE;
- goto ExecuteCaseEnd;
- }
- Options = SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT |
- SHELL_EXPANSION_OPTION_NO_QUOTE_REMOVAL;
- //
- // Loop through every case and see if any of the sets of patterns match.
- //
- CurrentSetEntry = Node->U.Case.PatternList.Next;
- while (CurrentSetEntry != &(Node->U.Case.PatternList)) {
- Set = LIST_VALUE(CurrentSetEntry, SHELL_CASE_PATTERN_SET, ListEntry);
- CurrentSetEntry = CurrentSetEntry->Next;
- //
- // Loop through every pattern in the set.
- //
- CurrentPatternEntry = Set->PatternEntryList.Next;
- while (CurrentPatternEntry != &(Set->PatternEntryList)) {
- PatternEntry = LIST_VALUE(CurrentPatternEntry,
- SHELL_CASE_PATTERN_ENTRY,
- ListEntry);
- CurrentPatternEntry = CurrentPatternEntry->Next;
- Result = ShPerformExpansions(Shell,
- PatternEntry->Pattern,
- PatternEntry->PatternSize,
- Options,
- &ExpandedPattern,
- &ExpandedPatternSize,
- NULL,
- NULL);
- if (Result == FALSE) {
- goto ExecuteCaseEnd;
- }
- ShStringDequote(ExpandedPattern,
- ExpandedPatternSize,
- SHELL_DEQUOTE_FOR_PATTERN_MATCHING,
- &ExpandedPatternSize);
- Match = SwDoesPatternMatch(Input,
- InputSize,
- ExpandedPattern,
- ExpandedPatternSize);
- free(ExpandedPattern);
- //
- // If the input matches the case, run the action associated with
- // it and end the case.
- //
- if (Match != FALSE) {
- Result = TRUE;
- if (Set->Action != NULL) {
- Result = ShExecuteNode(Shell, Set->Action);
- }
- goto ExecuteCaseEnd;
- }
- }
- }
- ExecuteCaseEnd:
- if (Input != NULL) {
- free(Input);
- }
- //
- // If no case was executed, the return value is zero.
- //
- if (Match == FALSE) {
- Shell->ReturnValue = 0;
- }
- return Result;
- }
- BOOL
- ShExecuteWhileOrUntil (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a while statement or an until statement.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- BOOL BeenAround;
- PSHELL_NODE Condition;
- INT ConditionResult;
- PSHELL_NODE DoGroup;
- BOOL ExecuteDoGroup;
- PSHELL_NODE Node;
- BOOL Result;
- Node = ExecutionNode->Node;
- assert((Node->Type == ShellNodeWhile) || (Node->Type == ShellNodeUntil));
- //
- // Get the condition, true, and maybe the false conditions.
- //
- assert(Node->Children.Next != &(Node->Children));
- Condition = LIST_VALUE(Node->Children.Next, SHELL_NODE, SiblingListEntry);
- assert(Condition->SiblingListEntry.Next != &(Node->Children));
- DoGroup = LIST_VALUE(Condition->SiblingListEntry.Next,
- SHELL_NODE,
- SiblingListEntry);
- //
- // Execute the do-group as long as the condition is zero for while loops
- // or non-zero for until loops.
- //
- BeenAround = FALSE;
- while (TRUE) {
- Result = ShExecuteNode(Shell, Condition);
- if (Result == FALSE) {
- return FALSE;
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- ConditionResult = Shell->LastReturnValue;
- Shell->ReturnValue = 0;
- //
- // Break out if no longer on the execution stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- return TRUE;
- }
- //
- // Figure out whether or not to execute the do group.
- //
- ExecuteDoGroup = FALSE;
- if (Node->Type == ShellNodeWhile) {
- if (ConditionResult == 0) {
- ExecuteDoGroup = TRUE;
- }
- } else {
- if (ConditionResult != 0) {
- ExecuteDoGroup = TRUE;
- }
- }
- //
- // If the do-group isn't going to be executed and never has before,
- // the return value is zero. Otherwise the return value is left as the
- // last command in the do-group.
- //
- if (ExecuteDoGroup == FALSE) {
- if (BeenAround == FALSE) {
- Shell->ReturnValue = 0;
- }
- break;
- }
- //
- // Run the do-group.
- //
- ExecutionNode->Flags |= SHELL_EXECUTION_BODY;
- Result = ShExecuteNode(Shell, DoGroup);
- ExecutionNode->Flags &= ~SHELL_EXECUTION_BODY;
- if (Result == FALSE) {
- return FALSE;
- }
- //
- // Break out if no longer on the execution stack, otherwise loop
- // around and run the condition again.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- return TRUE;
- }
- }
- return Result;
- }
- BOOL
- ShExecuteSubshellGroup (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine executes a subshell compound statement, which is a compound
- list inside of parentheses.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- ExecutionNode - Supplies a pointer to the node to execute.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Child;
- pid_t ChildProcess;
- PLIST_ENTRY CurrentEntry;
- PSHELL_NODE Node;
- PSTR OriginalDirectory;
- BOOL Result;
- INT Status;
- PSHELL Subshell;
- pid_t WaitResult;
- Node = ExecutionNode->Node;
- OriginalDirectory = NULL;
- assert(Node->Type == ShellNodeSubshell);
- Subshell = ShCreateSubshell(Shell, NULL, 0, FALSE);
- if (Subshell == NULL) {
- return FALSE;
- }
- ChildProcess = -1;
- if (SwForkSupported != FALSE) {
- ChildProcess = SwFork();
- } else {
- //
- // Save the current directory.
- //
- OriginalDirectory = getcwd(NULL, 0);
- }
- //
- // Execute all the children on the subshell (either if this is the child
- // process or fork never happened.
- //
- if (ChildProcess <= 0) {
- CurrentEntry = Node->Children.Next;
- while (CurrentEntry != &(Node->Children)) {
- Child = LIST_VALUE(CurrentEntry, SHELL_NODE, SiblingListEntry);
- CurrentEntry = CurrentEntry->Next;
- Result = ShExecuteNode(Subshell, Child);
- if (Result == FALSE) {
- goto ExecuteSubshellGroupEnd;
- }
- if (Shell->Exited != FALSE) {
- break;
- }
- //
- // Break out if the execution node was removed from the stack.
- //
- if (ExecutionNode->ListEntry.Next == NULL) {
- break;
- }
- }
- }
- //
- // If this is the child process, exit now.
- //
- if (ChildProcess == 0) {
- exit(Subshell->LastReturnValue);
- //
- // If this is the parent process, wait for the child.
- //
- } else if (ChildProcess > 0) {
- WaitResult = SwWaitPid(ChildProcess, 0, &(Subshell->LastReturnValue));
- if (WaitResult == -1) {
- Subshell->LastReturnValue = SHELL_ERROR_OPEN;
- SwPrintError(errno,
- NULL,
- "Failed to wait for pid %d",
- ChildProcess);
- Result = FALSE;
- goto ExecuteSubshellGroupEnd;
- }
- ShOsConvertExitStatus(&(Subshell->LastReturnValue));
- }
- Shell->ReturnValue = Subshell->LastReturnValue;
- Result = TRUE;
- ExecuteSubshellGroupEnd:
- if (Subshell != NULL) {
- ShDestroyShell(Subshell);
- }
- //
- // Restore the current directory.
- //
- if (OriginalDirectory != NULL) {
- Status = chdir(OriginalDirectory);
- if (Status != 0) {
- Result = FALSE;
- }
- free(OriginalDirectory);
- }
- return Result;
- }
- VOID
- ShExitOnError (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine exits the shell if the most recent simple command failed,
- unless the simple command is part of a compound list inside a while, until,
- or if, is part of an And-Or list, or is a pipeline with a bang.
- Arguments:
- Shell - Supplies a pointer to the shell containing the node.
- Return Value:
- None.
- --*/
- {
- PLIST_ENTRY CurrentEntry;
- PSHELL_EXECUTION_NODE ExecutionNode;
- SHELL_NODE_TYPE Type;
- if ((Shell->Exited != FALSE) || (Shell->ReturnValue == 0)) {
- return;
- }
- CurrentEntry = Shell->ExecutionStack.Next;
- while (CurrentEntry != &(Shell->ExecutionStack)) {
- ExecutionNode = LIST_VALUE(CurrentEntry,
- SHELL_EXECUTION_NODE,
- ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // If an if/while/until is found with the body executing, an And-Or
- // is found, or a pipeline is found with a bang then don't exit, just
- // return.
- //
- Type = ExecutionNode->Node->Type;
- if (((Type == ShellNodeIf) || (Type == ShellNodeWhile) ||
- (Type == ShellNodeUntil) || (Type == ShellNodeAndOr)) &&
- ((ExecutionNode->Flags & SHELL_EXECUTION_BODY) == 0)) {
- return;
- }
- if ((Type == ShellNodePipeline) &&
- (ExecutionNode->Node->U.Pipeline.Bang != FALSE)) {
- return;
- }
- }
- //
- // None of the conditions were met, so exit this shell.
- //
- Shell->Exited = TRUE;
- return;
- }
- BOOL
- ShApplyRedirections (
- PSHELL Shell,
- PSHELL_EXECUTION_NODE ExecutionNode
- )
- /*++
- Routine Description:
- This routine applies any redirections to the current command.
- Arguments:
- Shell - Supplies a pointer to the shell.
- ExecutionNode - Supplies a pointer to the execution node to apply
- redirections of.
- Return Value:
- TRUE on success.
- --*/
- {
- PSHELL_ACTIVE_REDIRECT ActiveRedirect;
- PSTR AfterScan;
- PLIST_ENTRY CurrentEntry;
- PSTR DocumentText;
- UINTN DocumentTextSize;
- PSTR ExpandedFileName;
- UINTN ExpandedFileNameSize;
- PSTR ExpandedString;
- UINTN ExpandedStringSize;
- PSHELL_HERE_DOCUMENT HereDocument;
- INT NewDescriptor;
- INT NewDescriptorAnywhere;
- INT OpenFlags;
- ULONG Options;
- INT OriginalDescriptor;
- unsigned long PathSize;
- INT Pipe[2];
- PSHELL_IO_REDIRECT Redirect;
- BOOL Result;
- INT SourceFileNumber;
- SHELL_IO_REDIRECTION_TYPE Type;
- INT WriteCopy;
- ActiveRedirect = NULL;
- ExpandedFileName = NULL;
- ExpandedFileNameSize = 0;
- ExpandedString = NULL;
- Pipe[0] = -1;
- Pipe[1] = -1;
- //
- // Loop through all the redirections.
- //
- CurrentEntry = ExecutionNode->Node->RedirectList.Next;
- while (CurrentEntry != &(ExecutionNode->Node->RedirectList)) {
- Redirect = LIST_VALUE(CurrentEntry, SHELL_IO_REDIRECT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- Type = Redirect->Type;
- //
- // Allocate an active redirect entry.
- //
- ActiveRedirect = malloc(sizeof(SHELL_ACTIVE_REDIRECT));
- if (ActiveRedirect == NULL) {
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- ActiveRedirect->ChildProcessId = -1;
- //
- // Expand the file name.
- //
- if (Redirect->FileName != NULL) {
- Options = SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT;
- Result = ShPerformExpansions(Shell,
- Redirect->FileName,
- Redirect->FileNameSize,
- Options,
- &ExpandedFileName,
- &ExpandedFileNameSize,
- NULL,
- NULL);
- if (Result == FALSE) {
- goto ApplyRedirectionsEnd;
- }
- //
- // Let the OS layer play with the path if it wants.
- //
- PathSize = ExpandedFileNameSize;
- Result = ShFixUpPath(&ExpandedFileName, &PathSize);
- if (Result == FALSE) {
- goto ApplyRedirectionsEnd;
- }
- }
- //
- // Perform normal file redirections.
- //
- if ((Type == ShellRedirectRead) ||
- (Type == ShellRedirectWrite) ||
- (Type == ShellRedirectAppend) ||
- (Type == ShellRedirectReadWrite) ||
- (Type == ShellRedirectClobber)) {
- OpenFlags = O_CREAT | O_BINARY;
- if ((Shell->Options & SHELL_OPTION_NO_CLOBBER) != 0) {
- OpenFlags |= O_EXCL;
- }
- if (Type == ShellRedirectRead) {
- OpenFlags |= O_RDONLY;
- OpenFlags &= ~(O_CREAT | O_EXCL);
- } else if (Type == ShellRedirectWrite) {
- OpenFlags |= O_WRONLY | O_TRUNC;
- } else if (Type == ShellRedirectAppend) {
- OpenFlags |= O_WRONLY | O_APPEND;
- } else if (Type == ShellRedirectReadWrite) {
- OpenFlags |= O_RDWR;
- } else if (Type == ShellRedirectClobber) {
- OpenFlags |= O_WRONLY | O_TRUNC;
- OpenFlags &= ~O_EXCL;
- }
- //
- // Open up the file.
- //
- NewDescriptorAnywhere = open(ExpandedFileName,
- OpenFlags,
- SHELL_FILE_CREATION_MASK);
- if (NewDescriptorAnywhere < 0) {
- PRINT_ERROR("sh: Unable to open redirection file %s: %s.\n",
- ExpandedFileName,
- strerror(errno));
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- //
- // Copy the original descriptor somewhere, then close the
- // descriptor and copy the newly opened file into it.
- //
- OriginalDescriptor = ShDup(Shell, Redirect->FileNumber, FALSE);
- if (NewDescriptorAnywhere != Redirect->FileNumber) {
- NewDescriptor = ShDup2(Shell,
- NewDescriptorAnywhere,
- Redirect->FileNumber);
- if (NewDescriptor < 0) {
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- ShClose(Shell, NewDescriptorAnywhere);
- }
- //
- // Perform redirections from other file descriptors.
- //
- } else if ((Type == ShellRedirectReadFromDescriptor) ||
- (Type == ShellRedirectWriteToDescriptor)) {
- //
- // If the source file number evaluates to -, then the file number is
- // closed.
- //
- if (strcmp(ExpandedFileName, "-") == 0) {
- OriginalDescriptor = ShDup(Shell, Redirect->FileNumber, FALSE);
- ShClose(Shell, Redirect->FileNumber);
- } else {
- SourceFileNumber = strtol(ExpandedFileName, &AfterScan, 10);
- if ((SourceFileNumber < 0) || (AfterScan == ExpandedFileName)) {
- PRINT_ERROR("sh: Bad file descriptor number '%s'.",
- ExpandedFileName);
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- //
- // Copy the original descriptor, then close the destination and
- // copy the source in there.
- //
- OriginalDescriptor = ShDup(Shell, Redirect->FileNumber, FALSE);
- if (Redirect->FileNumber != SourceFileNumber) {
- NewDescriptor = ShDup2(Shell,
- SourceFileNumber,
- Redirect->FileNumber);
- if (NewDescriptor < 0) {
- PRINT_ERROR("sh: Unable to duplicate file %d.\n",
- SourceFileNumber);
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- }
- }
- //
- // Perform a redirection from a here document.
- //
- } else if ((Type == ShellRedirectHereDocument) ||
- (Type == ShellRedirectStrippedHereDocument)) {
- HereDocument = Redirect->HereDocument;
- DocumentText = HereDocument->Document;
- DocumentTextSize = HereDocument->DocumentSize;
- //
- // Perform expansions on the here document.
- //
- if (Redirect->HereDocument->EndWordWasQuoted == FALSE) {
- Options = SHELL_EXPANSION_OPTION_NO_TILDE_EXPANSION |
- SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT;
- Result = ShPerformExpansions(Shell,
- DocumentText,
- DocumentTextSize,
- Options,
- &ExpandedString,
- &ExpandedStringSize,
- NULL,
- NULL);
- if (Result == FALSE) {
- goto ApplyRedirectionsEnd;
- }
- DocumentText = ExpandedString;
- DocumentTextSize = ExpandedStringSize;
- }
- //
- // Create a pipe for the here document and wire up the file
- // descriptor to the read end of the pipe.
- //
- Result = ShCreatePipe(Pipe);
- if (Result == FALSE) {
- goto ApplyRedirectionsEnd;
- }
- OriginalDescriptor = ShDup(Shell, Redirect->FileNumber, FALSE);
- if (OriginalDescriptor == -1) {
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- //
- // Copy the write descriptor out of range of the shell standard
- // descriptors, since on Windows the write side is just a thread,
- // so it stays open.
- //
- if (SwForkSupported == 0) {
- WriteCopy = ShDup(Shell, Pipe[1], 0);
- ShClose(Shell, Pipe[1]);
- Pipe[1] = WriteCopy;
- }
- //
- // Launch the subprocess or thread to feed the document text into
- // the descriptor.
- //
- assert(DocumentTextSize != 0);
- DocumentTextSize -= 1;
- Result = ShPushInputText(DocumentText, DocumentTextSize, Pipe);
- if (Result < 0) {
- goto ApplyRedirectionsEnd;
- }
- Pipe[1] = -1;
- ShDup2(Shell, Pipe[0], Redirect->FileNumber);
- ShClose(Shell, Pipe[0]);
- Pipe[0] = -1;
- if (Result > 0) {
- ActiveRedirect->ChildProcessId = Result;
- }
- } else {
- assert(FALSE);
- Result = FALSE;
- goto ApplyRedirectionsEnd;
- }
- if (Redirect->FileNumber == STDOUT_FILENO) {
- fflush(stdout);
- } else if (Redirect->FileNumber == STDERR_FILENO) {
- fflush(stderr);
- }
- //
- // Initialize the active redirect so that the original descriptor can
- // be restored.
- //
- ActiveRedirect->FileNumber = Redirect->FileNumber;
- ActiveRedirect->OriginalDescriptor = OriginalDescriptor;
- INSERT_BEFORE(&(ActiveRedirect->ListEntry),
- &(ExecutionNode->ActiveRedirectList));
- ActiveRedirect = NULL;
- //
- // Free the expanded file name.
- //
- if (ExpandedFileName != NULL) {
- free(ExpandedFileName);
- ExpandedFileName = NULL;
- }
- }
- Result = TRUE;
- ApplyRedirectionsEnd:
- if (Pipe[0] != -1) {
- ShClose(Shell, Pipe[0]);
- }
- if (Pipe[1] != -1) {
- ShClose(Shell, Pipe[1]);
- }
- if (ExpandedString != NULL) {
- free(ExpandedString);
- }
- if (ExpandedFileName != NULL) {
- free(ExpandedFileName);
- }
- if (Result == FALSE) {
- if (ActiveRedirect != NULL) {
- free(ActiveRedirect);
- }
- }
- return Result;
- }
|