1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- parser.c
- Abstract:
- This module implements the shell grammar parser.
- Author:
- Evan Green 5-Jun-2013
- Environment:
- User Mode
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "sh.h"
- #include "shparse.h"
- #include "../swlib.h"
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <assert.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- BOOL
- ShParseCompleteCommand (
- PSHELL Shell,
- PSHELL_NODE *CompleteCommandNode
- );
- PSHELL_NODE
- ShParseList (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseAndOr (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParsePipeline (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseCommand (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseCompoundCommand (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseBraceGroup (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseSubshell (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseIf (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseWhileOrUntil (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseFor (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseCase (
- PSHELL Shell
- );
- BOOL
- ShParsePattern (
- PSHELL Shell,
- PSHELL_NODE Case,
- PSHELL_CASE_PATTERN_SET *NewPatternSet
- );
- PSHELL_NODE
- ShParseDoGroup (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseCompoundList (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseTerm (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseSimpleCommandOrFunction (
- PSHELL Shell
- );
- PSHELL_NODE
- ShParseSimpleCommand (
- PSHELL Shell,
- PSTR FirstWord,
- UINTN FirstWordSize
- );
- PSHELL_NODE
- ShParseFunctionDefinition (
- PSHELL Shell,
- PSTR FunctionName,
- UINTN FunctionNameSize
- );
- BOOL
- ShParseOptionalRedirectList (
- PSHELL Shell,
- PSHELL_NODE Node
- );
- BOOL
- ShParseRedirection (
- PSHELL Shell,
- PSHELL_NODE Node
- );
- BOOL
- ShParseAssignment (
- PSHELL Shell,
- PSHELL_NODE Node
- );
- BOOL
- ShParseLineBreak (
- PSHELL Shell,
- BOOL Required,
- BOOL FirstCommandWord
- );
- BOOL
- ShParseSeparator (
- PSHELL Shell,
- PCHAR Separator
- );
- BOOL
- ShParseSeparatorOp (
- PSHELL Shell,
- PCHAR Separator
- );
- PSHELL_NODE
- ShCreateNode (
- PSHELL Shell,
- SHELL_NODE_TYPE Type
- );
- VOID
- ShDeleteNode (
- PSHELL_NODE Node
- );
- VOID
- ShPrintNode (
- PSHELL Shell,
- PSHELL_NODE Node,
- ULONG Depth
- );
- BOOL
- ShCreateRedirection (
- PSHELL Shell,
- PSHELL_NODE Node,
- SHELL_IO_REDIRECTION_TYPE Type,
- INT FileNumber,
- PSTR FileName,
- UINTN FileNameSize
- );
- VOID
- ShDestroyRedirection (
- PSHELL_IO_REDIRECT Redirect
- );
- BOOL
- ShCreateAssignment (
- PSHELL_NODE Node,
- PSTR Name,
- UINTN NameSize,
- PSTR Value,
- UINTN ValueSize
- );
- VOID
- ShDestroyAssignment (
- PSHELL_ASSIGNMENT Assignment
- );
- BOOL
- ShAddPatternToSet (
- PSHELL_CASE_PATTERN_SET Set,
- PSTR Pattern,
- UINTN PatternSize
- );
- VOID
- ShDestroyCasePatternList (
- PSHELL_NODE CaseNode
- );
- BOOL
- ShAddComponentToCommand (
- PSHELL_NODE Command,
- PSTR Component,
- UINTN ComponentSize
- );
- BOOL
- ShIsStringQuoted (
- PSTR String
- );
- VOID
- ShParseError (
- PSHELL Shell,
- PSHELL_NODE Node,
- PSTR Format,
- ...
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- BOOL ShDebugPrintParseTree = FALSE;
- //
- // ------------------------------------------------------------------ Functions
- //
- BOOL
- ShParse (
- PSHELL Shell,
- PSHELL_NODE *Node
- )
- /*++
- Routine Description:
- This routine attempts to parse a complete command out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Node - Supplies a pointer where the parsed node representing the command
- will be returned.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE Command;
- BOOL Result;
- Command = NULL;
- //
- // Prime the lexer.
- //
- if (Shell->Lexer.LexerPrimed == FALSE) {
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseEnd;
- }
- Shell->Lexer.LexerPrimed = TRUE;
- }
- Result = ShParseCompleteCommand(Shell, &Command);
- if ((Result == FALSE) &&
- ((Shell->Options & SHELL_OPTION_INTERACTIVE) == 0)) {
- PRINT_ERROR("sh: Failed to parse command.\n");
- }
- if ((Command != NULL) && (ShDebugPrintParseTree != FALSE)) {
- ShPrintNode(Shell, Command, 0);
- }
- ParseEnd:
- *Node = Command;
- return Result;
- }
- VOID
- ShDestroyHereDocument (
- PSHELL_HERE_DOCUMENT HereDocument
- )
- /*++
- Routine Description:
- This routine destroys a here document. It assumes the here document is
- already removed from any list it was on.
- Arguments:
- HereDocument - Supplies a pointer to the here document to destroy.
- Return Value:
- None.
- --*/
- {
- if (HereDocument->EndWord != NULL) {
- free(HereDocument->EndWord);
- }
- if (HereDocument->Document != NULL) {
- free(HereDocument->Document);
- }
- free(HereDocument);
- return;
- }
- VOID
- ShRetainNode (
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine increases the reference count on a node.
- Arguments:
- Node - Supplies a pointer to the node to retain.
- Return Value:
- None.
- --*/
- {
- assert((Node->ReferenceCount > 0) && (Node->ReferenceCount < 0x1000));
- Node->ReferenceCount += 1;
- return;
- }
- VOID
- ShReleaseNode (
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine releases a reference on a shell node. If this causes the
- reference count to drop to zero then the node is destroyed.
- Arguments:
- Node - Supplies a pointer to the node to release.
- Return Value:
- None.
- --*/
- {
- assert((Node->ReferenceCount > 0) && (Node->ReferenceCount < 0x1000));
- Node->ReferenceCount -= 1;
- if (Node->ReferenceCount == 0) {
- ShDeleteNode(Node);
- }
- return;
- }
- BOOL
- ShIsName (
- PSTR String,
- UINTN StringSize
- )
- /*++
- Routine Description:
- This routine determines if the given string is a valid NAME according to
- the shell grammar.
- Arguments:
- String - Supplies a pointer to the candidate string.
- StringSize - Supplies the number of characters to check.
- Return Value:
- TRUE if the string is a valid name.
- FALSE if the string is not a valid name.
- --*/
- {
- UCHAR Character;
- ULONG Index;
- if (StringSize == 0) {
- return FALSE;
- }
- //
- // The first character can be A through Z or an underscore.
- //
- Character = *String;
- if (!SHELL_NAME_FIRST_CHARACTER(Character)) {
- return FALSE;
- }
- for (Index = 1; Index < StringSize; Index += 1) {
- Character = String[Index];
- if (Character == '\0') {
- break;
- }
- if (!SHELL_NAME_CHARACTER(Character)) {
- return FALSE;
- }
- }
- return TRUE;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- BOOL
- ShParseCompleteCommand (
- PSHELL Shell,
- PSHELL_NODE *CompleteCommandNode
- )
- /*++
- Routine Description:
- This routine attempts to parse a complete command out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- CompleteCommandNode - Supplies a pointer where the complete command will
- be returned on success. The caller is responsible for destroying this
- node.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_NODE List;
- BOOL Result;
- CHAR SeparatorOp;
- Result = FALSE;
- List = NULL;
- //
- // Get rid of any extraneous newlines.
- //
- while (Shell->Lexer.TokenType == '\n') {
- ShCheckForSignals(Shell);
- ShPrintPrompt(Shell, 1);
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseCompleteCommandEnd;
- }
- }
- if (Shell->Lexer.TokenType == TOKEN_END_OF_FILE) {
- Result = TRUE;
- goto ParseCompleteCommandEnd;
- }
- List = ShParseList(Shell);
- if (List == NULL) {
- Result = FALSE;
- goto ParseCompleteCommandEnd;
- }
- Result = ShParseSeparatorOp(Shell, &SeparatorOp);
- if ((Result != FALSE) && (SeparatorOp == '&')) {
- List->RunInBackground = TRUE;
- }
- Result = TRUE;
- ParseCompleteCommandEnd:
- if (Result == FALSE) {
- if (List != NULL) {
- ShReleaseNode(List);
- List = NULL;
- }
- }
- *CompleteCommandNode = List;
- return Result;
- }
- PSHELL_NODE
- ShParseList (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a list out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE AndOr;
- PSHELL_NODE List;
- BOOL Result;
- CHAR SeparatorOp;
- Result = FALSE;
- List = NULL;
- //
- // Parse and-or statements separated by separator operators.
- //
- while (TRUE) {
- AndOr = ShParseAndOr(Shell);
- if (AndOr == NULL) {
- if (List == NULL) {
- Result = FALSE;
- ShParseError(Shell, NULL, "Unexpected token.");
- goto ParseListEnd;
- }
- break;
- }
- Result = ShParseSeparatorOp(Shell, &SeparatorOp);
- //
- // If this is the first time around and there's only one item, then
- // just return that item.
- //
- if ((Result == FALSE) && (List == NULL)) {
- List = AndOr;
- break;
- }
- //
- // There's more than one and-or. If the listhas yet to be made, make it
- // now.
- //
- if (List == NULL) {
- List = ShCreateNode(Shell, ShellNodeList);
- if (List == NULL) {
- goto ParseListEnd;
- }
- }
- INSERT_BEFORE(&(AndOr->SiblingListEntry), &(List->Children));
- //
- // Regardless of where the command was, if there's no separator, this
- // loop is done.
- //
- if (Result == FALSE) {
- break;
- }
- if (SeparatorOp == '&') {
- AndOr->RunInBackground = TRUE;
- }
- }
- AndOr = NULL;
- Result = TRUE;
- ParseListEnd:
- if (AndOr != NULL) {
- ShReleaseNode(AndOr);
- }
- if (Result == FALSE) {
- if (List != NULL) {
- ShReleaseNode(List);
- List = NULL;
- }
- }
- return List;
- }
- PSHELL_NODE
- ShParseAndOr (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse an and-or statement out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE AndOr;
- PSHELL_NODE Pipeline;
- BOOL Result;
- ULONG SeparatorToken;
- Result = FALSE;
- AndOr = NULL;
- //
- // Parse pipeline statements separated by separator operators.
- //
- while (TRUE) {
- Pipeline = ShParsePipeline(Shell);
- if (Pipeline == NULL) {
- goto ParseAndOrEnd;
- }
- SeparatorToken = Shell->Lexer.TokenType;
- //
- // If this is the first time around and there's only one item, then
- // just return that item.
- //
- if ((SeparatorToken != TOKEN_DOUBLE_AND) &&
- (SeparatorToken != TOKEN_DOUBLE_OR) &&
- (AndOr == NULL)) {
- AndOr = Pipeline;
- break;
- }
- //
- // There's more than one pipeline. If the and-or has yet to be
- // made, make it now.
- //
- if (AndOr == NULL) {
- AndOr = ShCreateNode(Shell, ShellNodeAndOr);
- if (AndOr == NULL) {
- goto ParseAndOrEnd;
- }
- }
- INSERT_BEFORE(&(Pipeline->SiblingListEntry), &(AndOr->Children));
- //
- // Regardless of where the pipeline was, if there's no valid AND or OR,
- // stop.
- //
- if ((SeparatorToken != TOKEN_DOUBLE_AND) &&
- (SeparatorToken != TOKEN_DOUBLE_OR)) {
- break;
- }
- Pipeline->AndOr = SeparatorToken;
- Pipeline = NULL;
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseAndOrEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- if (Result == FALSE) {
- goto ParseAndOrEnd;
- }
- }
- Pipeline = NULL;
- Result = TRUE;
- ParseAndOrEnd:
- if (Pipeline != NULL) {
- ShReleaseNode(Pipeline);
- }
- if (Result == FALSE) {
- if (AndOr != NULL) {
- ShReleaseNode(AndOr);
- AndOr = NULL;
- }
- }
- return AndOr;
- }
- PSHELL_NODE
- ShParsePipeline (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a pipeline out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Command;
- PSHELL_NODE Pipeline;
- BOOL Result;
- Command = NULL;
- Pipeline = NULL;
- Result = FALSE;
- //
- // Parse an optional ! at the beginning.
- //
- if (Shell->Lexer.TokenType == '!') {
- Pipeline = ShCreateNode(Shell, ShellNodePipeline);
- if (Pipeline == NULL) {
- goto ParsePipelineEnd;
- }
- Pipeline->U.Pipeline.Bang = TRUE;
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParsePipelineEnd;
- }
- }
- //
- // Parse pipeline statements separated by separator operators.
- //
- while (TRUE) {
- Command = ShParseCommand(Shell);
- if (Command == NULL) {
- Result = FALSE;
- goto ParsePipelineEnd;
- }
- //
- // If this is the first time around and there's only one item, then
- // just return that item.
- //
- if ((Shell->Lexer.TokenType != '|') && (Pipeline == NULL)) {
- Pipeline = Command;
- break;
- }
- //
- // There's more than one command. If the pipeline has yet to be made,
- // make it now.
- //
- if (Pipeline == NULL) {
- Pipeline = ShCreateNode(Shell, ShellNodePipeline);
- if (Pipeline == NULL) {
- goto ParsePipelineEnd;
- }
- }
- INSERT_BEFORE(&(Command->SiblingListEntry), &(Pipeline->Children));
- //
- // Regardless of where the command was, if there's no valid pipe
- // character then stop. Otherwise, move over the pipe character.
- //
- if (Shell->Lexer.TokenType != '|') {
- break;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- Command = NULL;
- goto ParsePipelineEnd;
- }
- //
- // It's known now that most recent command wasn't the last, so set it
- // to run in the background so the shell doesn't wait on it.
- //
- Command->RunInBackground = TRUE;
- ShParseLineBreak(Shell, FALSE, TRUE);
- }
- Command = NULL;
- Result = TRUE;
- ParsePipelineEnd:
- if (Command != NULL) {
- ShReleaseNode(Command);
- }
- if (Result == FALSE) {
- if (Pipeline != NULL) {
- ShReleaseNode(Pipeline);
- Pipeline = NULL;
- }
- }
- return Pipeline;
- }
- PSHELL_NODE
- ShParseCommand (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a command.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Command;
- //
- // A command is either a simple command, compound command, or function
- // definition.
- //
- switch (Shell->Lexer.TokenType) {
- case '{':
- case '(':
- case TOKEN_FOR:
- case TOKEN_CASE:
- case TOKEN_IF:
- case TOKEN_WHILE:
- case TOKEN_UNTIL:
- Command = ShParseCompoundCommand(Shell);
- break;
- case TOKEN_ASSIGNMENT_WORD:
- case TOKEN_IO_NUMBER:
- case TOKEN_LESS_THAN_AND:
- case TOKEN_GREATER_THAN_AND:
- case TOKEN_DOUBLE_GREATER_THAN:
- case TOKEN_DOUBLE_LESS_THAN:
- case TOKEN_DOUBLE_LESS_THAN_DASH:
- case '>':
- case '<':
- Command = ShParseSimpleCommand(Shell, NULL, 0);
- break;
- case TOKEN_WORD:
- Command = ShParseSimpleCommandOrFunction(Shell);
- break;
- default:
- Command = NULL;
- break;
- }
- return Command;
- }
- PSHELL_NODE
- ShParseCompoundCommand (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a command.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Command;
- BOOL Result;
- Command = NULL;
- Result = TRUE;
- //
- // A command is either a simple command, compound command, or function
- // definition.
- //
- switch (Shell->Lexer.TokenType) {
- case '{':
- Command = ShParseBraceGroup(Shell);
- break;
- case '(':
- Command = ShParseSubshell(Shell);
- break;
- case TOKEN_FOR:
- Command = ShParseFor(Shell);
- break;
- case TOKEN_CASE:
- Command = ShParseCase(Shell);
- break;
- case TOKEN_IF:
- Command = ShParseIf(Shell);
- break;
- case TOKEN_WHILE:
- case TOKEN_UNTIL:
- Command = ShParseWhileOrUntil(Shell);
- break;
- default:
- ShParseError(Shell, NULL, "Unexpected token for compound command.");
- break;
- }
- if (Command == NULL) {
- return NULL;
- }
- //
- // Compound commands can optionally have a redirect list on them too.
- //
- Result = ShParseOptionalRedirectList(Shell, Command);
- if (Result == FALSE) {
- goto ParseCompoundCommandEnd;
- }
- ParseCompoundCommandEnd:
- if (Result == FALSE) {
- if (Command != NULL) {
- ShReleaseNode(Command);
- Command = NULL;
- }
- }
- return Command;
- }
- PSHELL_NODE
- ShParseBraceGroup (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a set of statements surrounded by a brace
- group.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE BraceGroup;
- PSHELL_NODE CompoundList;
- BOOL Result;
- //
- // Scan over the open brace.
- //
- assert(Shell->Lexer.TokenType == '{');
- BraceGroup = ShCreateNode(Shell, ShellNodeBraceGroup);
- if (BraceGroup == NULL) {
- Result = FALSE;
- goto ParseBraceGroupEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseBraceGroupEnd;
- }
- //
- // Get the tender compound list inside.
- //
- CompoundList = ShParseCompoundList(Shell);
- if (CompoundList == NULL) {
- Result = FALSE;
- goto ParseBraceGroupEnd;
- }
- INSERT_BEFORE(&(CompoundList->SiblingListEntry), &(BraceGroup->Children));
- //
- // Scan over the closing brace.
- //
- if (Shell->Lexer.TokenType != '}') {
- ShParseError(Shell,
- NULL,
- "Expected '}' for open brace at line %d.",
- BraceGroup->LineNumber);
- Result = FALSE;
- goto ParseBraceGroupEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseBraceGroupEnd;
- }
- ParseBraceGroupEnd:
- if (Result == FALSE) {
- if (BraceGroup != NULL) {
- ShReleaseNode(BraceGroup);
- BraceGroup = NULL;
- }
- }
- return BraceGroup;
- }
- PSHELL_NODE
- ShParseSubshell (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a subshell sequence, which is basically of
- the form ( compound list ).
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE CompoundList;
- PSHELL_NODE Node;
- BOOL Result;
- Node = ShCreateNode(Shell, ShellNodeSubshell);
- if (Node == NULL) {
- Result = FALSE;
- goto ParseSubshellEnd;
- }
- assert(Shell->Lexer.TokenType == '(');
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseSubshellEnd;
- }
- CompoundList = ShParseCompoundList(Shell);
- if (CompoundList == NULL) {
- Result = FALSE;
- goto ParseSubshellEnd;
- }
- INSERT_BEFORE(&(CompoundList->SiblingListEntry), &(Node->Children));
- if (Shell->Lexer.TokenType != ')') {
- ShParseError(Shell,
- NULL,
- "Expected ')' for subshell at line %d.",
- Node->LineNumber);
- Result = FALSE;
- goto ParseSubshellEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseSubshellEnd;
- }
- Result = TRUE;
- ParseSubshellEnd:
- if (Result == FALSE) {
- if (Node != NULL) {
- ShReleaseNode(Node);
- Node = NULL;
- }
- }
- return Node;
- }
- PSHELL_NODE
- ShParseIf (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse an if statement.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Condition;
- PSHELL_NODE ElseBody;
- PSHELL_NODE Node;
- BOOL Result;
- BOOL SwallowFi;
- PSHELL_NODE ThenCompoundList;
- assert((Shell->Lexer.TokenType == TOKEN_IF) ||
- (Shell->Lexer.TokenType == TOKEN_ELIF));
- //
- // Don't swallow the "fi" if this is an elif.
- //
- SwallowFi = TRUE;
- if (Shell->Lexer.TokenType == TOKEN_ELIF) {
- SwallowFi = FALSE;
- }
- Node = NULL;
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseIfEnd;
- }
- Node = ShCreateNode(Shell, ShellNodeIf);
- if (Node == NULL) {
- Result = FALSE;
- goto ParseIfEnd;
- }
- //
- // Parse out the condition part of the if statement.
- //
- Condition = ShParseCompoundList(Shell);
- if (Condition == NULL) {
- Result = FALSE;
- goto ParseIfEnd;
- }
- INSERT_BEFORE(&(Condition->SiblingListEntry), &(Node->Children));
- //
- // The next thing had better be a "then".
- //
- if (Shell->Lexer.TokenType != TOKEN_THEN) {
- ShParseError(Shell,
- NULL,
- "Expected 'then' for if at line %d.",
- Node->LineNumber);
- Result = FALSE;
- goto ParseIfEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseIfEnd;
- }
- //
- // Parse the compound list of stuff to do if the condition succeeds.
- //
- ThenCompoundList = ShParseCompoundList(Shell);
- if (ThenCompoundList == NULL) {
- Result = FALSE;
- goto ParseIfEnd;
- }
- INSERT_BEFORE(&(ThenCompoundList->SiblingListEntry), &(Node->Children));
- //
- // Ok, so there's an else or elif. If it's an else, parse out the
- // compound list that follows. For elifs, pretend it's a brand new if
- // statement inside the else.
- //
- if ((Shell->Lexer.TokenType == TOKEN_ELSE) ||
- (Shell->Lexer.TokenType == TOKEN_ELIF)) {
- if (Shell->Lexer.TokenType == TOKEN_ELSE) {
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseIfEnd;
- }
- ElseBody = ShParseCompoundList(Shell);
- } else {
- ElseBody = ShParseIf(Shell);
- }
- if (ElseBody == NULL) {
- Result = FALSE;
- goto ParseIfEnd;
- }
- INSERT_BEFORE(&(ElseBody->SiblingListEntry), &(Node->Children));
- }
- //
- // There had better be a fi at the end.
- //
- if (Shell->Lexer.TokenType != TOKEN_FI) {
- ShParseError(Shell,
- NULL,
- "Expected 'fi' for if at line %d.",
- Node->LineNumber);
- Result = FALSE;
- goto ParseIfEnd;
- }
- if (SwallowFi != FALSE) {
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseIfEnd;
- }
- }
- ParseIfEnd:
- if (Result == FALSE) {
- if (Node != NULL) {
- ShReleaseNode(Node);
- }
- Node = NULL;
- }
- return Node;
- }
- PSHELL_NODE
- ShParseWhileOrUntil (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse either a while statement or an until
- statement.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Condition;
- PSHELL_NODE DoGroup;
- PSHELL_NODE Node;
- SHELL_NODE_TYPE NodeType;
- BOOL Result;
- assert((Shell->Lexer.TokenType == TOKEN_WHILE) ||
- (Shell->Lexer.TokenType == TOKEN_UNTIL));
- if (Shell->Lexer.TokenType == TOKEN_WHILE) {
- NodeType = ShellNodeWhile;
- } else {
- NodeType = ShellNodeUntil;
- }
- Node = ShCreateNode(Shell, NodeType);
- if (Node == NULL) {
- Result = FALSE;
- goto ParseWhileOrUntilEnd;
- }
- //
- // Skip over the while or until.
- //
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseWhileOrUntilEnd;
- }
- //
- // Parse the compound list that is the condition.
- //
- Condition = ShParseCompoundList(Shell);
- if (Condition == NULL) {
- Result = FALSE;
- goto ParseWhileOrUntilEnd;
- }
- INSERT_BEFORE(&(Condition->SiblingListEntry), &(Node->Children));
- //
- // Parse the do group of stuff to do inside the loop.
- //
- DoGroup = ShParseDoGroup(Shell);
- if (DoGroup == NULL) {
- Result = FALSE;
- goto ParseWhileOrUntilEnd;
- }
- INSERT_BEFORE(&(DoGroup->SiblingListEntry), &(Node->Children));
- Result = TRUE;
- ParseWhileOrUntilEnd:
- if (Result == FALSE) {
- if (Node != NULL) {
- ShReleaseNode(Node);
- Node = NULL;
- }
- }
- return Node;
- }
- PSHELL_NODE
- ShParseFor (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a for statement.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE DoGroup;
- PSHELL_NODE ForNode;
- BOOL LineBreakRequired;
- BOOL Result;
- assert(Shell->Lexer.TokenType == TOKEN_FOR);
- ForNode = NULL;
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- //
- // The next token is the name of the iterator variable.
- //
- if (Shell->Lexer.TokenType != TOKEN_WORD) {
- ShParseError(Shell, NULL, "Expected 'for' variable iterator name.");
- Result = FALSE;
- goto ParseForEnd;
- }
- Result = ShIsName(Shell->Lexer.TokenBuffer, Shell->Lexer.TokenBufferSize);
- if (Result == FALSE) {
- ShParseError(Shell, NULL, "Bad for loop variable name.");
- goto ParseForEnd;
- }
- ForNode = ShCreateNode(Shell, ShellNodeFor);
- if (ForNode == NULL) {
- Result = FALSE;
- goto ParseForEnd;
- }
- ForNode->U.For.Name = SwStringDuplicate(Shell->Lexer.TokenBuffer,
- Shell->Lexer.TokenBufferSize);
- if (ForNode->U.For.Name == NULL) {
- Result = FALSE;
- goto ParseForEnd;
- }
- ForNode->U.For.NameSize = Shell->Lexer.TokenBufferSize;
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- ShParseLineBreak(Shell, FALSE, FALSE);
- //
- // There can be an "in" next, but it's optional (the command line is used
- // if nothing's supplied).
- //
- LineBreakRequired = FALSE;
- if (Shell->Lexer.TokenType == TOKEN_IN) {
- LineBreakRequired = TRUE;
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- //
- // Now there's an optional wordlist, which means as long as words are
- // coming in add them to the wordlist.
- //
- while (SHELL_TOKEN_WORD_LIKE(Shell->Lexer.TokenType)) {
- Result = ShStringAppend(&(ForNode->U.For.WordListBuffer),
- &(ForNode->U.For.WordListBufferSize),
- &(ForNode->U.For.WordListBufferCapacity),
- Shell->Lexer.TokenBuffer,
- Shell->Lexer.TokenBufferSize);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- }
- }
- //
- // Now parse a sequential separator, which is either a semicolon and
- // a linebreak or one or more newlines.
- //
- if (Shell->Lexer.TokenType == ';') {
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, FALSE);
- } else {
- Result = ShParseLineBreak(Shell, LineBreakRequired, FALSE);
- }
- if (Result == FALSE) {
- goto ParseForEnd;
- }
- //
- // Parse out a "do group" and call it a day.
- //
- DoGroup = ShParseDoGroup(Shell);
- if (DoGroup == NULL) {
- Result = FALSE;
- goto ParseForEnd;
- }
- INSERT_BEFORE(&(DoGroup->SiblingListEntry), &(ForNode->Children));
- Result = TRUE;
- ParseForEnd:
- if (Result == FALSE) {
- if (ForNode != NULL) {
- ShReleaseNode(ForNode);
- ForNode = NULL;
- }
- }
- return ForNode;
- }
- PSHELL_NODE
- ShParseCase (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a case statement.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE CaseNode;
- PSHELL_CASE_PATTERN_SET PatternSet;
- BOOL Result;
- CaseNode = NULL;
- assert(Shell->Lexer.TokenType == TOKEN_CASE);
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // After the word case comes the name of the thing to match against.
- //
- if (!SHELL_TOKEN_WORD_LIKE(Shell->Lexer.TokenType)) {
- ShParseError(Shell, NULL, "Expected case input word.");
- Result = FALSE;
- goto ParseCaseEnd;
- }
- CaseNode = ShCreateNode(Shell, ShellNodeCase);
- if (CaseNode == NULL) {
- Result = FALSE;
- goto ParseCaseEnd;
- }
- CaseNode->U.Case.Name = SwStringDuplicate(Shell->Lexer.TokenBuffer,
- Shell->Lexer.TokenBufferSize);
- if (CaseNode->U.Case.Name == NULL) {
- Result = FALSE;
- goto ParseCaseEnd;
- }
- CaseNode->U.Case.NameSize = Shell->Lexer.TokenBufferSize;
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Then parse a linebreak.
- //
- Result = ShParseLineBreak(Shell, FALSE, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Then parse an "in" and another linebreak.
- //
- if (Shell->Lexer.TokenType != TOKEN_IN) {
- ShParseError(Shell, CaseNode, "Expected 'in'.");
- Result = FALSE;
- goto ParseCaseEnd;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Here comes the case list, which is actually optional altogether.
- //
- while (TRUE) {
- //
- // Parse past an optional opening parentheses.
- //
- if (Shell->Lexer.TokenType == '(') {
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- }
- //
- // Scan the pattern(s).
- //
- Result = ShParsePattern(Shell, CaseNode, &PatternSet);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // If the pattern set is null, this is an esac, stop parsing patterns.
- //
- if (PatternSet == NULL) {
- assert(Shell->Lexer.TokenType == TOKEN_ESAC);
- break;
- }
- //
- // Scan the closing parentheses.
- //
- if (Shell->Lexer.TokenType != ')') {
- ShParseError(Shell, NULL, "Expected ')' to close case pattern.");
- Result = FALSE;
- goto ParseCaseEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Scan an optional linebreak.
- //
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Now there's either a compound list there, a double semicolon, or an
- // esac.
- //
- if (Shell->Lexer.TokenType == TOKEN_DOUBLE_SEMICOLON) {
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- } else if (Shell->Lexer.TokenType == TOKEN_ESAC) {
- break;
- //
- // It must be a compound list.
- //
- } else {
- PatternSet->Action = ShParseCompoundList(Shell);
- if (PatternSet->Action == NULL) {
- goto ParseCaseEnd;
- }
- //
- // Parse an optional linebreak.
- //
- Result = ShParseLineBreak(Shell, FALSE, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- //
- // Now there had better be an esac or a double semicolon there.
- //
- if (Shell->Lexer.TokenType == TOKEN_ESAC) {
- break;
- } else if (Shell->Lexer.TokenType != TOKEN_DOUBLE_SEMICOLON) {
- ShParseError(Shell,
- NULL,
- "Expected ';;' for case at line %d.",
- CaseNode->LineNumber);
- Result = FALSE;
- goto ParseCaseEnd;
- }
- //
- // Scan over the double semicolon and an optional linebreak.
- //
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, FALSE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- }
- }
- //
- // Scan over the esac.
- //
- if (Shell->Lexer.TokenType != TOKEN_ESAC) {
- ShParseError(Shell,
- NULL,
- "Expected 'esac' for case at line %d.",
- CaseNode->LineNumber);
- Result = FALSE;
- goto ParseCaseEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseCaseEnd;
- }
- ParseCaseEnd:
- if (Result == FALSE) {
- if (CaseNode != NULL) {
- ShReleaseNode(CaseNode);
- CaseNode = NULL;
- }
- }
- return CaseNode;
- }
- BOOL
- ShParsePattern (
- PSHELL Shell,
- PSHELL_NODE Case,
- PSHELL_CASE_PATTERN_SET *NewPatternSet
- )
- /*++
- Routine Description:
- This routine attempts to scan a pattern, which is a sequence of 1 or more
- words separated by bars (|).
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Case - Supplies a pointer to the case node where the pattern will be added.
- NewPatternSet - Supplies a pointer where the new pattern set will be
- returned on success.
- Return Value:
- Returns a pointer to the created case pattern on success.
- NULL on failure.
- --*/
- {
- BOOL GotSomething;
- BOOL Result;
- PSHELL_CASE_PATTERN_SET Set;
- assert(Case->Type == ShellNodeCase);
- //
- // Create a pattern set and optimistically add it to the case statement.
- //
- Set = malloc(sizeof(SHELL_CASE_PATTERN_SET));
- if (Set == NULL) {
- return FALSE;
- }
- memset(Set, 0, sizeof(SHELL_CASE_PATTERN_SET));
- INITIALIZE_LIST_HEAD(&(Set->PatternEntryList));
- INSERT_BEFORE(&(Set->ListEntry), &(Case->U.Case.PatternList));
- //
- // Loop through and try to get at least one pattern word out.
- //
- GotSomething = FALSE;
- while (SHELL_TOKEN_WORD_LIKE(Shell->Lexer.TokenType)) {
- //
- // If it's an esac on the first pattern, then treat it with respect.
- //
- if ((Shell->Lexer.TokenType == TOKEN_ESAC) && (GotSomething == FALSE)) {
- break;
- }
- //
- // Add the new pattern word to this set.
- //
- Result = ShAddPatternToSet(Set,
- Shell->Lexer.TokenBuffer,
- Shell->Lexer.TokenBufferSize);
- if (Result == FALSE) {
- goto ParsePatternEnd;
- }
- GotSomething = TRUE;
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParsePatternEnd;
- }
- //
- // If the next thing isn't a pipe, then this pattern list is over.
- //
- if (Shell->Lexer.TokenType != '|') {
- break;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParsePatternEnd;
- }
- }
- //
- // If it was an esac and nothing else, take the set back off the list, this
- // case statement is over.
- //
- if ((GotSomething == FALSE) && (Shell->Lexer.TokenType == TOKEN_ESAC)) {
- assert((Set->Action == NULL) && (LIST_EMPTY(&(Set->PatternEntryList))));
- LIST_REMOVE(&(Set->ListEntry));
- free(Set);
- Set = NULL;
- Result = TRUE;
- } else {
- Result = GotSomething;
- if (Result == FALSE) {
- ShParseError(Shell, Case, "Expected pattern word.");
- }
- }
- ParsePatternEnd:
- *NewPatternSet = Set;
- return Result;
- }
- PSHELL_NODE
- ShParseDoGroup (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a "do group", which consists of "do", a
- compound list, and "done". This is used in most loop constructs.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE CompoundList;
- ULONG DoLineNumber;
- BOOL Result;
- CompoundList = NULL;
- ShParseLineBreak(Shell, FALSE, FALSE);
- //
- // The first thing in a do group should really be a do.
- //
- if (Shell->Lexer.TokenType != TOKEN_DO) {
- ShParseError(Shell, NULL, "Expected 'do'.");
- Result = FALSE;
- goto ParseDoGroupEnd;
- }
- DoLineNumber = Shell->Lexer.LineNumber;
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseDoGroupEnd;
- }
- //
- // Now get the compound list.
- //
- CompoundList = ShParseCompoundList(Shell);
- if (CompoundList == NULL) {
- goto ParseDoGroupEnd;
- }
- //
- // Now there should be a done.
- //
- if (Shell->Lexer.TokenType != TOKEN_DONE) {
- ShParseError(Shell,
- NULL,
- "Expected 'done' for 'do' at line %d.",
- DoLineNumber);
- Result = FALSE;
- goto ParseDoGroupEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseDoGroupEnd;
- }
- ParseDoGroupEnd:
- if (Result == FALSE) {
- if (CompoundList != NULL) {
- ShReleaseNode(CompoundList);
- CompoundList = NULL;
- }
- }
- return CompoundList;
- }
- PSHELL_NODE
- ShParseCompoundList (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a compound list.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE CompoundList;
- BOOL Result;
- CHAR Separator;
- CompoundList = NULL;
- //
- // Parse a line break (really just an optional newline list).
- //
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- if (Result == FALSE) {
- goto ParseCompoundListEnd;
- }
- //
- // Parse a term, call it a compound list.
- //
- CompoundList = ShParseTerm(Shell);
- if (CompoundList == NULL) {
- Result = FALSE;
- goto ParseCompoundListEnd;
- }
- //
- // Parse an optional separator.
- //
- Result = ShParseSeparator(Shell, &Separator);
- if (Result != FALSE) {
- if (Separator == '&') {
- CompoundList->RunInBackground = TRUE;
- }
- }
- Result = TRUE;
- ParseCompoundListEnd:
- if (Result == FALSE) {
- if (CompoundList != NULL) {
- ShReleaseNode(CompoundList);
- CompoundList = NULL;
- }
- }
- return CompoundList;
- }
- PSHELL_NODE
- ShParseTerm (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a term out of the shell input.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE AndOr;
- BOOL Result;
- CHAR SeparatorOp;
- PSHELL_NODE Term;
- Result = FALSE;
- Term = NULL;
- //
- // Parse and-or statements separated by separators (this is different than
- // a list where the separators were separator ops only).
- //
- while (TRUE) {
- AndOr = ShParseAndOr(Shell);
- if (AndOr == NULL) {
- if (Term == NULL) {
- Result = FALSE;
- ShParseError(Shell, NULL, "Unexpected token.");
- goto ParseTermEnd;
- }
- break;
- }
- Result = ShParseSeparator(Shell, &SeparatorOp);
- //
- // If this is the first time around and there's only one item, then
- // just return that item.
- //
- if ((Result == FALSE) && (Term == NULL)) {
- Term = AndOr;
- break;
- }
- //
- // There's more than one and-or. If the list has yet to be made, make it
- // now.
- //
- if (Term == NULL) {
- Term = ShCreateNode(Shell, ShellNodeTerm);
- if (Term == NULL) {
- goto ParseTermEnd;
- }
- }
- INSERT_BEFORE(&(AndOr->SiblingListEntry), &(Term->Children));
- //
- // Regardless of where the command was, if there's no separator, this
- // loop is done.
- //
- if (Result == FALSE) {
- break;
- }
- if (SeparatorOp == '&') {
- AndOr->RunInBackground = TRUE;
- }
- }
- AndOr = NULL;
- Result = TRUE;
- ParseTermEnd:
- if (AndOr != NULL) {
- ShReleaseNode(AndOr);
- }
- if (Result == FALSE) {
- if (Term != NULL) {
- ShReleaseNode(Term);
- Term = NULL;
- }
- }
- return Term;
- }
- PSHELL_NODE
- ShParseSimpleCommandOrFunction (
- PSHELL Shell
- )
- /*++
- Routine Description:
- This routine attempts to parse a simple command or function definition.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Command;
- PSTR FirstWord;
- UINTN FirstWordSize;
- BOOL Result;
- Command = NULL;
- //
- // Get the first word and look beyond it.
- //
- FirstWord = NULL;
- FirstWordSize = 0;
- if (Shell->Lexer.TokenType == TOKEN_WORD) {
- FirstWordSize = Shell->Lexer.TokenBufferSize;
- FirstWord = SwStringDuplicate(Shell->Lexer.TokenBuffer, FirstWordSize);
- if (FirstWord == NULL) {
- Result = FALSE;
- goto ParseSimpleCommandOrFunctionEnd;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseSimpleCommandOrFunctionEnd;
- }
- }
- //
- // If the next thing is an open parentheses, then it's a function
- // definition. Otherwise it's a simple command.
- //
- if ((ShIsName(FirstWord, FirstWordSize) != FALSE) &&
- (Shell->Lexer.TokenType == '(')) {
- Command = ShParseFunctionDefinition(Shell, FirstWord, FirstWordSize);
- } else {
- Command = ShParseSimpleCommand(Shell, FirstWord, FirstWordSize);
- }
- ParseSimpleCommandOrFunctionEnd:
- if (FirstWord != NULL) {
- free(FirstWord);
- }
- return Command;
- }
- PSHELL_NODE
- ShParseSimpleCommand (
- PSHELL Shell,
- PSTR FirstWord,
- UINTN FirstWordSize
- )
- /*++
- Routine Description:
- This routine attempts to parse a simple command.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- FirstWord - Supplies an optional pointer to the command name that was
- parsed out earlier.
- FirstWordSize - Supplies the size of the first word buffer in bytes
- including the null terminator.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- BOOL AllowAssignmentWords;
- PSHELL_NODE Command;
- BOOL NonEmpty;
- BOOL Result;
- BOOL SwallowToken;
- NonEmpty = FALSE;
- Result = TRUE;
- Command = ShCreateNode(Shell, ShellNodeSimpleCommand);
- if (Command == NULL) {
- return NULL;
- }
- AllowAssignmentWords = TRUE;
- if (FirstWord != NULL) {
- AllowAssignmentWords = FALSE;
- Result = ShAddComponentToCommand(Command, FirstWord, FirstWordSize);
- if (Result == FALSE) {
- goto ParseSimpleCommandEnd;
- }
- NonEmpty = TRUE;
- }
- while (TRUE) {
- SwallowToken = TRUE;
- switch (Shell->Lexer.TokenType) {
- case TOKEN_IO_NUMBER:
- case TOKEN_LESS_THAN_AND:
- case TOKEN_GREATER_THAN_AND:
- case TOKEN_DOUBLE_GREATER_THAN:
- case TOKEN_DOUBLE_LESS_THAN:
- case TOKEN_DOUBLE_LESS_THAN_DASH:
- case TOKEN_LESS_THAN_GREATER_THAN:
- case TOKEN_CLOBBER:
- case '>':
- case '<':
- Result = ShParseRedirection(Shell, Command);
- if (Result == FALSE) {
- goto ParseSimpleCommandEnd;
- }
- SwallowToken = FALSE;
- break;
- case TOKEN_ASSIGNMENT_WORD:
- //
- // If still at that phase (before the initial command word) parse
- // any assignment words that come out. If the assignment word was
- // miscategorized, fall through to the regular word processing.
- //
- if (AllowAssignmentWords != FALSE) {
- Result = ShParseAssignment(Shell, Command);
- if (Result != FALSE) {
- break;
- }
- Result = TRUE;
- }
- //
- // If assignment words are not allowed or the assignment word
- // failed, fall through.
- //
- //
- // Inside of a simple command, they keywords are just regular arguments.
- //
- case TOKEN_IF:
- case TOKEN_THEN:
- case TOKEN_ELSE:
- case TOKEN_ELIF:
- case TOKEN_FI:
- case TOKEN_DO:
- case TOKEN_DONE:
- case TOKEN_CASE:
- case TOKEN_ESAC:
- case TOKEN_WHILE:
- case TOKEN_UNTIL:
- case TOKEN_FOR:
- case TOKEN_IN:
- case TOKEN_WORD:
- case '!':
- case '{':
- case '}':
- Result = ShAddComponentToCommand(Command,
- Shell->Lexer.TokenBuffer,
- Shell->Lexer.TokenBufferSize);
- if (Result == FALSE) {
- goto ParseSimpleCommandEnd;
- }
- AllowAssignmentWords = FALSE;
- break;
- default:
- Result = NonEmpty;
- if (Result == FALSE) {
- ShParseError(Shell, Command, "Expected simple command word.");
- }
- goto ParseSimpleCommandEnd;
- }
- NonEmpty = TRUE;
- if (SwallowToken != FALSE) {
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseSimpleCommandEnd;
- }
- }
- }
- ParseSimpleCommandEnd:
- if (Result == FALSE) {
- if (Command != NULL) {
- ShReleaseNode(Command);
- Command = NULL;
- }
- }
- return Command;
- }
- PSHELL_NODE
- ShParseFunctionDefinition (
- PSHELL Shell,
- PSTR FunctionName,
- UINTN FunctionNameSize
- )
- /*++
- Routine Description:
- This routine attempts to parse a function definition.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- FunctionName - Supplies a pointer to the function name.
- FunctionNameSize - Supplies the size of the function name buffer in bytes
- including the null terminator.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE Body;
- PSHELL_NODE Function;
- BOOL Result;
- Result = TRUE;
- Function = ShCreateNode(Shell, ShellNodeFunction);
- if (Function == NULL) {
- return NULL;
- }
- Function->U.Function.Name = SwStringDuplicate(FunctionName,
- FunctionNameSize);
- if (Function->U.Function.Name == NULL) {
- Result = FALSE;
- goto ParseFunctionDefinitionEnd;
- }
- Function->U.Function.NameSize = FunctionNameSize;
- //
- // The current token should be an open parentheses, and then there should
- // be a close parenthese and a newline.
- //
- assert(Shell->Lexer.TokenType == '(');
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseFunctionDefinitionEnd;
- }
- if (Shell->Lexer.TokenType != ')') {
- ShParseError(Shell,
- Function,
- "Expected ')' for function definition.");
- Result = FALSE;
- goto ParseFunctionDefinitionEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseFunctionDefinitionEnd;
- }
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- if (Result == FALSE) {
- goto ParseFunctionDefinitionEnd;
- }
- Body = ShParseCompoundCommand(Shell);
- if (Body == NULL) {
- goto ParseFunctionDefinitionEnd;
- }
- INSERT_BEFORE(&(Body->SiblingListEntry), &(Function->Children));
- //
- // Parse an optional redirect list on the function body.
- //
- Result = ShParseOptionalRedirectList(Shell, Function);
- if (Result == FALSE) {
- goto ParseFunctionDefinitionEnd;
- }
- Result = TRUE;
- ParseFunctionDefinitionEnd:
- if (Result == FALSE) {
- if (Function != NULL) {
- ShReleaseNode(Function);
- Function = NULL;
- }
- }
- return Function;
- }
- BOOL
- ShParseOptionalRedirectList (
- PSHELL Shell,
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine attempts to parse a redirect list that may or may not be there.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Node - Supplies a pointer to the node the I/O redirection belongs to.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- BOOL BreakOut;
- BOOL Result;
- Result = TRUE;
- BreakOut = FALSE;
- while (BreakOut == FALSE) {
- switch (Shell->Lexer.TokenType) {
- case TOKEN_IO_NUMBER:
- case TOKEN_LESS_THAN_AND:
- case TOKEN_GREATER_THAN_AND:
- case TOKEN_DOUBLE_GREATER_THAN:
- case TOKEN_DOUBLE_LESS_THAN:
- case TOKEN_DOUBLE_LESS_THAN_DASH:
- case TOKEN_LESS_THAN_GREATER_THAN:
- case TOKEN_CLOBBER:
- case '>':
- case '<':
- Result = ShParseRedirection(Shell, Node);
- if (Result == FALSE) {
- goto ParseOptionalRedirectListEnd;
- }
- break;
- default:
- BreakOut = TRUE;
- break;
- }
- }
- ParseOptionalRedirectListEnd:
- return Result;
- }
- BOOL
- ShParseRedirection (
- PSHELL Shell,
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine attempts to parse an I/O redirection.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Node - Supplies a pointer to the node the I/O redirection belongs to.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- ULONG DefaultFileNumber;
- PSTR FileName;
- UINTN FileNameSize;
- ULONG FileNumber;
- BOOL Result;
- SHELL_IO_REDIRECTION_TYPE Type;
- DefaultFileNumber = -1;
- FileNumber = -1;
- Type = ShellRedirectInvalid;
- //
- // Start by attempting to get an I/O number.
- //
- if (Shell->Lexer.TokenType == TOKEN_IO_NUMBER) {
- FileNumber = strtol(Shell->Lexer.TokenBuffer, NULL, 10);
- if (FileNumber < 0) {
- //
- // The lexer should have stripped off any piece that couldn't be
- // interpreted as a positive number, so it's weird that this
- // number wouldn't convert.
- //
- assert(FALSE);
- Result = FALSE;
- goto ParseRedirectionEnd;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseRedirectionEnd;
- }
- }
- //
- // Now get the operator.
- //
- switch (Shell->Lexer.TokenType) {
- case TOKEN_LESS_THAN_AND:
- Type = ShellRedirectReadFromDescriptor;
- DefaultFileNumber = STDIN_FILENO;
- break;
- case TOKEN_GREATER_THAN_AND:
- Type = ShellRedirectWriteToDescriptor;
- DefaultFileNumber = STDOUT_FILENO;
- break;
- case TOKEN_DOUBLE_GREATER_THAN:
- Type = ShellRedirectAppend;
- DefaultFileNumber = STDOUT_FILENO;
- break;
- case TOKEN_DOUBLE_LESS_THAN:
- Type = ShellRedirectHereDocument;
- DefaultFileNumber = STDIN_FILENO;
- break;
- case TOKEN_DOUBLE_LESS_THAN_DASH:
- Type = ShellRedirectStrippedHereDocument;
- DefaultFileNumber = STDIN_FILENO;
- break;
- case TOKEN_LESS_THAN_GREATER_THAN:
- Type = ShellRedirectReadWrite;
- DefaultFileNumber = STDIN_FILENO;
- break;
- case TOKEN_CLOBBER:
- Type = ShellRedirectClobber;
- DefaultFileNumber = STDOUT_FILENO;
- break;
- case '>':
- Type = ShellRedirectWrite;
- DefaultFileNumber = STDOUT_FILENO;
- break;
- case '<':
- Type = ShellRedirectRead;
- DefaultFileNumber = STDIN_FILENO;
- break;
- default:
- Result = FALSE;
- goto ParseRedirectionEnd;
- }
- Result = ShGetToken(Shell, FALSE);
- if (Result == FALSE) {
- goto ParseRedirectionEnd;
- }
- //
- // Now get the word of where to redirect to. Convert to a file descriptor
- // number if needed.
- //
- if (!SHELL_TOKEN_WORD_LIKE(Shell->Lexer.TokenType)) {
- ShParseError(Shell, Node, "Expected redirection file name.");
- Result = FALSE;
- goto ParseRedirectionEnd;
- }
- FileName = Shell->Lexer.TokenBuffer;
- FileNameSize = Shell->Lexer.TokenBufferSize;
- if (FileNumber == -1) {
- FileNumber = DefaultFileNumber;
- }
- Result = ShCreateRedirection(Shell,
- Node,
- Type,
- FileNumber,
- FileName,
- FileNameSize);
- if (Result == FALSE) {
- goto ParseRedirectionEnd;
- }
- Result = ShGetToken(Shell, TRUE);
- if (Result == FALSE) {
- goto ParseRedirectionEnd;
- }
- ParseRedirectionEnd:
- return Result;
- }
- BOOL
- ShParseAssignment (
- PSHELL Shell,
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine attempts to parse an assignment word.
- Arguments:
- Shell - Supplies a pointer to the shell object.
- Node - Supplies a pointer to the node the assignment belongs to.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSTR Name;
- UINTN NameSize;
- BOOL Result;
- PSTR Token;
- UINTN TokenSize;
- PSTR Value;
- UINTN ValueSize;
- Token = Shell->Lexer.TokenBuffer;
- TokenSize = Shell->Lexer.TokenBufferSize;
- Name = Token;
- Value = strchr(Token, '=');
- if (Value == NULL) {
- //
- // How would something get here unless this was already categorized as
- // an assignment word?
- //
- assert(FALSE);
- return FALSE;
- }
- //
- // There can't be a zero length name.
- //
- if (Value == Token) {
- return FALSE;
- }
- NameSize = ((UINTN)Value - (UINTN)Token);
- if (ShIsName(Name, NameSize) == FALSE) {
- return FALSE;
- }
- NameSize += 1;
- Value += 1;
- ValueSize = TokenSize - ((UINTN)Value - (UINTN)Token);
- Result = ShCreateAssignment(Node, Name, NameSize, Value, ValueSize);
- return Result;
- }
- BOOL
- ShParseLineBreak (
- PSHELL Shell,
- BOOL Required,
- BOOL FirstCommandWord
- )
- /*++
- Routine Description:
- This routine parses a line break term, which is just zero or more newline.
- Arguments:
- Shell - Supplies a pointer to the shell whose input is being parsed.
- Required - Supplies a boolean indicating if at least one line break is
- required.
- FirstCommandWord - Supplies the boolean that will be passed to the get
- token routine indicating whether or not this next token could be the
- command word of a simple command.
- Return Value:
- TRUE on success.
- FALSE if the lexer failed to get a token.
- --*/
- {
- BOOL Result;
- if ((Required != FALSE) && (Shell->Lexer.TokenType != '\n')) {
- return FALSE;
- }
- while (Shell->Lexer.TokenType == '\n') {
- ShPrintPrompt(Shell, 2);
- Result = ShGetToken(Shell, FirstCommandWord);
- if (Result == FALSE) {
- return FALSE;
- }
- }
- return TRUE;
- }
- BOOL
- ShParseSeparator (
- PSHELL Shell,
- PCHAR Separator
- )
- /*++
- Routine Description:
- This routine attempts to parse a separator, which is either a separator op
- followed by a linebreak, or 1+ newlines.
- Arguments:
- Shell - Supplies a pointer to the shell whose input is being parsed.
- Separator - Supplies a pointer where the separator character will be
- returned on success, or 0 if there was no separator.
- Return Value:
- TRUE if a separator operator was parsed.
- FALSE if there was no separator or the lexer failed to move on.
- --*/
- {
- BOOL Result;
- //
- // First try to get a separator op and linebreak. A linebreak swallows a
- // newline list.
- //
- Result = ShParseSeparatorOp(Shell, Separator);
- if (Result != FALSE) {
- Result = ShParseLineBreak(Shell, FALSE, TRUE);
- //
- // There's no separator op, try to get at least one newline.
- //
- } else {
- Result = ShParseLineBreak(Shell, TRUE, TRUE);
- }
- return Result;
- }
- BOOL
- ShParseSeparatorOp (
- PSHELL Shell,
- PCHAR Separator
- )
- /*++
- Routine Description:
- This routine attempts to parse a separator operator, which is either a
- semicolon or an ampersand.
- Arguments:
- Shell - Supplies a pointer to the shell whose input is being parsed.
- Separator - Supplies a pointer where the separator character will be
- returned on success, or 0 if there was no separator.
- Return Value:
- TRUE if a separator operator was parsed.
- FALSE if there was no separator or the lexer failed to move on.
- --*/
- {
- BOOL Result;
- if ((Shell->Lexer.TokenType == ';') || (Shell->Lexer.TokenType == '&')) {
- *Separator = Shell->Lexer.TokenType;
- Result = ShGetToken(Shell, TRUE);
- return Result;
- }
- return FALSE;
- }
- PSHELL_NODE
- ShCreateNode (
- PSHELL Shell,
- SHELL_NODE_TYPE Type
- )
- /*++
- Routine Description:
- This routine allocates and initializes a shell node.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Type - Supplies the type of node to create.
- Return Value:
- Returns a pointer to the node on success.
- NULL on failure.
- --*/
- {
- PSHELL_NODE NewNode;
- NewNode = malloc(sizeof(SHELL_NODE));
- if (NewNode == NULL) {
- return NULL;
- }
- NewNode->Type = Type;
- NewNode->ReferenceCount = 1;
- NewNode->LineNumber = Shell->Lexer.LineNumber;
- if (Shell->Lexer.TokenType == '\n') {
- NewNode->LineNumber -= 1;
- }
- INITIALIZE_LIST_HEAD(&(NewNode->Children));
- INITIALIZE_LIST_HEAD(&(NewNode->RedirectList));
- NewNode->RunInBackground = FALSE;
- NewNode->AndOr = 0;
- switch (Type) {
- case ShellNodePipeline:
- NewNode->U.Pipeline.Bang = FALSE;
- break;
- case ShellNodeSimpleCommand:
- memset(&(NewNode->U.SimpleCommand),
- 0,
- sizeof(SHELL_NODE_SIMPLE_COMMAND));
- INITIALIZE_LIST_HEAD(&(NewNode->U.SimpleCommand.AssignmentList));
- break;
- case ShellNodeFunction:
- memset(&(NewNode->U.Function), 0, sizeof(SHELL_NODE_FUNCTION));
- break;
- case ShellNodeFor:
- memset(&(NewNode->U.For), 0, sizeof(SHELL_NODE_FOR));
- break;
- case ShellNodeCase:
- memset(&(NewNode->U.Case), 0, sizeof(SHELL_NODE_CASE));
- INITIALIZE_LIST_HEAD(&(NewNode->U.Case.PatternList));
- break;
- default:
- break;
- }
- return NewNode;
- }
- VOID
- ShDeleteNode (
- PSHELL_NODE Node
- )
- /*++
- Routine Description:
- This routine destroys a shell node.
- Arguments:
- Node - Supplies a pointer to the node to destroy.
- Return Value:
- None.
- --*/
- {
- PSHELL_ASSIGNMENT Assignment;
- PSHELL_NODE Child;
- PSHELL_IO_REDIRECT Redirect;
- assert(Node->ReferenceCount == 0);
- switch (Node->Type) {
- case ShellNodeSimpleCommand:
- while (LIST_EMPTY(&(Node->U.SimpleCommand.AssignmentList)) == FALSE) {
- Assignment = LIST_VALUE(Node->U.SimpleCommand.AssignmentList.Next,
- SHELL_ASSIGNMENT,
- ListEntry);
- ShDestroyAssignment(Assignment);
- }
- if (Node->U.SimpleCommand.Arguments != NULL) {
- free(Node->U.SimpleCommand.Arguments);
- }
- break;
- case ShellNodeFunction:
- if (Node->U.Function.Name != NULL) {
- free(Node->U.Function.Name);
- }
- break;
- case ShellNodeFor:
- if (Node->U.For.Name != NULL) {
- free(Node->U.For.Name);
- }
- if (Node->U.For.WordListBuffer != NULL) {
- free(Node->U.For.WordListBuffer);
- }
- break;
- case ShellNodeCase:
- ShDestroyCasePatternList(Node);
- if (Node->U.Case.Name != NULL) {
- free(Node->U.Case.Name);
- }
- break;
- default:
- break;
- }
- while (LIST_EMPTY(&(Node->RedirectList)) == FALSE) {
- Redirect = LIST_VALUE(Node->RedirectList.Next,
- SHELL_IO_REDIRECT,
- ListEntry);
- ShDestroyRedirection(Redirect);
- }
- while (LIST_EMPTY(&(Node->Children)) == FALSE) {
- Child = LIST_VALUE(Node->Children.Next, SHELL_NODE, SiblingListEntry);
- LIST_REMOVE(&(Child->SiblingListEntry));
- ShReleaseNode(Child);
- }
- free(Node);
- return;
- }
- VOID
- ShPrintNode (
- PSHELL Shell,
- PSHELL_NODE Node,
- ULONG Depth
- )
- /*++
- Routine Description:
- This routine prints out a parsed shell node.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Node - Supplies a pointer to the node to print.
- Depth - Supplies the indentation depth to print it at.
- Return Value:
- None.
- --*/
- {
- PSHELL_ASSIGNMENT Assignment;
- PSHELL_NODE Child;
- PLIST_ENTRY CurrentEntry;
- PLIST_ENTRY CurrentEntryEntry;
- ULONG DepthIndex;
- PSHELL_CASE_PATTERN_ENTRY Entry;
- PSHELL_IO_REDIRECT Redirect;
- PSHELL_CASE_PATTERN_SET Set;
- for (DepthIndex = 0; DepthIndex < Depth; DepthIndex += 1) {
- ShPrintTrace(Shell, " ");
- }
- ShPrintTrace(Shell, "Line %d ", Node->LineNumber);
- switch (Node->Type) {
- case ShellNodeInvalid:
- ShPrintTrace(Shell, "Invalid Node");
- break;
- case ShellNodeList:
- ShPrintTrace(Shell, "List");
- break;
- case ShellNodeAndOr:
- ShPrintTrace(Shell, "AndOr");
- break;
- case ShellNodePipeline:
- if (Node->U.Pipeline.Bang != FALSE) {
- ShPrintTrace(Shell, "! ");
- }
- ShPrintTrace(Shell, "Pipeline");
- break;
- case ShellNodeSimpleCommand:
- ShPrintTrace(Shell, "SimpleCommand:");
- CurrentEntry = Node->U.SimpleCommand.AssignmentList.Next;
- while (CurrentEntry != &(Node->U.SimpleCommand.AssignmentList)) {
- Assignment = LIST_VALUE(CurrentEntry, SHELL_ASSIGNMENT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- ShPrintTrace(Shell,
- " [%s]=[%s]",
- Assignment->Name,
- Assignment->Value);
- }
- ShPrintTrace(Shell, " [%s] ", Node->U.SimpleCommand.Arguments);
- break;
- case ShellNodeFunction:
- ShPrintTrace(Shell, "Function %s", Node->U.Function.Name);
- break;
- case ShellNodeIf:
- ShPrintTrace(Shell, "If");
- break;
- case ShellNodeTerm:
- ShPrintTrace(Shell, "Term");
- break;
- case ShellNodeFor:
- ShPrintTrace(Shell,
- "For [%s] in [%s]",
- Node->U.For.Name,
- Node->U.For.WordListBuffer);
- ShPrintTrace(Shell, " do");
- break;
- case ShellNodeBraceGroup:
- ShPrintTrace(Shell, "BraceGroup");
- break;
- case ShellNodeCase:
- ShPrintTrace(Shell, "Case [%s]", Node->U.Case.Name);
- break;
- case ShellNodeWhile:
- ShPrintTrace(Shell, "While");
- break;
- case ShellNodeUntil:
- ShPrintTrace(Shell, "Until");
- break;
- case ShellNodeSubshell:
- ShPrintTrace(Shell, "Subshell");
- break;
- default:
- assert(FALSE);
- break;
- }
- CurrentEntry = Node->RedirectList.Next;
- while (CurrentEntry != &(Node->RedirectList)) {
- Redirect = LIST_VALUE(CurrentEntry, SHELL_IO_REDIRECT, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- switch (Redirect->Type) {
- case ShellRedirectInvalid:
- ShPrintTrace(Shell, " INVALID_REDIRECT");
- break;
- case ShellRedirectRead:
- ShPrintTrace(Shell,
- " [%d<%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectReadFromDescriptor:
- ShPrintTrace(Shell,
- " [%d<&%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectWrite:
- ShPrintTrace(Shell,
- " [%d>%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectWriteToDescriptor:
- ShPrintTrace(Shell,
- " [%d>&%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectClobber:
- ShPrintTrace(Shell,
- " [%d>|%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectAppend:
- ShPrintTrace(Shell,
- " [%d>>%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectReadWrite:
- ShPrintTrace(Shell,
- " [%d<>%s]",
- Redirect->FileNumber,
- Redirect->FileName);
- break;
- case ShellRedirectHereDocument:
- ShPrintTrace(Shell,
- " [%d<<]>>>>\n",
- Redirect->FileNumber);
- ShPrintTrace(Shell,
- "%s\n<<<<",
- Redirect->HereDocument->Document);
- break;
- case ShellRedirectStrippedHereDocument:
- ShPrintTrace(Shell, " [%d<<-]>>>>\n", Redirect->FileNumber);
- ShPrintTrace(Shell, "%s\n<<<<", Redirect->HereDocument->Document);
- break;
- default:
- assert(FALSE);
- break;
- }
- }
- if (Node->RunInBackground != FALSE) {
- ShPrintTrace(Shell, " &");
- }
- if (Node->AndOr == TOKEN_DOUBLE_AND) {
- ShPrintTrace(Shell, " &&");
- } else if (Node->AndOr == TOKEN_DOUBLE_OR) {
- ShPrintTrace(Shell, " ||");
- }
- ShPrintTrace(Shell, "\n");
- if (Node->Type == ShellNodeCase) {
- //
- // Loop through every set in the case.
- //
- CurrentEntry = Node->U.Case.PatternList.Next;
- while (CurrentEntry != &(Node->U.Case.PatternList)) {
- Set = LIST_VALUE(CurrentEntry, SHELL_CASE_PATTERN_SET, ListEntry);
- CurrentEntry = CurrentEntry->Next;
- //
- // Loop through every entry in the set.
- //
- CurrentEntryEntry = Set->PatternEntryList.Next;
- while (CurrentEntryEntry != &(Set->PatternEntryList)) {
- Entry = LIST_VALUE(CurrentEntryEntry,
- SHELL_CASE_PATTERN_ENTRY,
- ListEntry);
- CurrentEntryEntry = CurrentEntryEntry->Next;
- for (DepthIndex = 0; DepthIndex < Depth + 1; DepthIndex += 1) {
- ShPrintTrace(Shell, " ");
- }
- ShPrintTrace(Shell, "Pattern: %s\n", Entry->Pattern);
- }
- if (Set->Action != NULL) {
- ShPrintNode(Shell, Set->Action, Depth + 2);
- } else {
- for (DepthIndex = 0; DepthIndex < Depth + 2; DepthIndex += 1) {
- ShPrintTrace(Shell, " ");
- }
- ShPrintTrace(Shell, "No Action");
- }
- }
- }
- CurrentEntry = Node->Children.Next;
- while (CurrentEntry != &(Node->Children)) {
- Child = LIST_VALUE(CurrentEntry, SHELL_NODE, SiblingListEntry);
- CurrentEntry = CurrentEntry->Next;
- ShPrintNode(Shell, Child, Depth + 1);
- }
- return;
- }
- BOOL
- ShCreateRedirection (
- PSHELL Shell,
- PSHELL_NODE Node,
- SHELL_IO_REDIRECTION_TYPE Type,
- INT FileNumber,
- PSTR FileName,
- UINTN FileNameSize
- )
- /*++
- Routine Description:
- This routine creates a new I/O redirection entry and puts it on the list
- for the given node.
- Arguments:
- Shell - Supplies a pointer to the shell the redirection is executing on.
- Node - Supplies a pointer to the shell node (probably a command).
- Type - Supplies the type of redirection.
- FileNumber - Supplies the file number being affected by the redirection.
- FileName - Supplies the optional name of the file being redirected to or
- from. If this parameter is not NULL, a copy of the given string will
- be made.
- FileNameSize - Supplies the size of the file name buffer in bytes.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_HERE_DOCUMENT HereDocument;
- PSHELL_IO_REDIRECT Redirect;
- BOOL Result;
- HereDocument = NULL;
- Redirect = malloc(sizeof(SHELL_IO_REDIRECT));
- if (Redirect == NULL) {
- return FALSE;
- }
- memset(Redirect, 0, sizeof(SHELL_IO_REDIRECT));
- Redirect->Type = Type;
- Redirect->FileNumber = FileNumber;
- //
- // If it's a here document, create a new structure and add it to the
- // lexer's list of pending here documents.
- //
- if ((Redirect->Type == ShellRedirectHereDocument) ||
- (Redirect->Type == ShellRedirectStrippedHereDocument)) {
- HereDocument = malloc(sizeof(SHELL_HERE_DOCUMENT));
- if (HereDocument == NULL) {
- Result = FALSE;
- goto CreateRedirectionEnd;
- }
- memset(HereDocument, 0, sizeof(SHELL_HERE_DOCUMENT));
- if (Redirect->Type == ShellRedirectStrippedHereDocument) {
- HereDocument->StripLeadingTabs = TRUE;
- }
- if (ShIsStringQuoted(FileName) != FALSE) {
- HereDocument->EndWordWasQuoted = TRUE;
- }
- HereDocument->EndWord = SwStringDuplicate(FileName, FileNameSize);
- if (HereDocument->EndWord == NULL) {
- Result = FALSE;
- goto CreateRedirectionEnd;
- }
- HereDocument->EndWordSize = FileNameSize;
- ShStringDequote(HereDocument->EndWord,
- HereDocument->EndWordSize,
- 0,
- &(HereDocument->EndWordSize));
- INSERT_BEFORE(&(HereDocument->ListEntry),
- &(Shell->Lexer.HereDocumentList));
- Redirect->HereDocument = HereDocument;
- } else if (FileName != NULL) {
- Redirect->FileNameSize = FileNameSize;
- Redirect->FileName = SwStringDuplicate(FileName, FileNameSize);
- if (Redirect->FileName == NULL) {
- Result = FALSE;
- goto CreateRedirectionEnd;
- }
- }
- INSERT_BEFORE(&(Redirect->ListEntry), &(Node->RedirectList));
- Result = TRUE;
- CreateRedirectionEnd:
- if (Result == FALSE) {
- if (HereDocument != NULL) {
- ShDestroyHereDocument(HereDocument);
- }
- if (Redirect != NULL) {
- ShDestroyRedirection(Redirect);
- }
- }
- return Result;
- }
- VOID
- ShDestroyRedirection (
- PSHELL_IO_REDIRECT Redirect
- )
- /*++
- Routine Description:
- This routine destroys an I/O redirection entry.
- Arguments:
- Redirect - Supplies a pointer to the redirect entry.
- Return Value:
- None.
- --*/
- {
- LIST_REMOVE(&(Redirect->ListEntry));
- if (Redirect->FileName != NULL) {
- free(Redirect->FileName);
- }
- if (Redirect->HereDocument != NULL) {
- if (Redirect->HereDocument->ListEntry.Next != NULL) {
- LIST_REMOVE(&(Redirect->HereDocument->ListEntry));
- }
- ShDestroyHereDocument(Redirect->HereDocument);
- }
- free(Redirect);
- return;
- }
- BOOL
- ShCreateAssignment (
- PSHELL_NODE Node,
- PSTR Name,
- UINTN NameSize,
- PSTR Value,
- UINTN ValueSize
- )
- /*++
- Routine Description:
- This routine creates an assignment structure.
- Arguments:
- Node - Supplies a pointer to the node to put the assignment on.
- Name - Supplies a pointer to the name string. A copy of this string will be
- made.
- NameSize - Supplies the length of the name string including a null
- terminator.
- Value - Supplies a pointer to the value string. A copy of this string will
- be made.
- ValueSize - Supplies the length of the value string including a null
- terminator.
- Return Value:
- TRUE on success.
- FALSE on allocation failure.
- --*/
- {
- PSHELL_ASSIGNMENT Assignment;
- BOOL Result;
- Result = FALSE;
- Assignment = malloc(sizeof(SHELL_ASSIGNMENT));
- if (Assignment == NULL) {
- return FALSE;
- }
- memset(Assignment, 0, sizeof(SHELL_ASSIGNMENT));
- Assignment->Name = SwStringDuplicate(Name, NameSize);
- if (Assignment->Name == NULL) {
- goto CreateAssignmentEnd;
- }
- Assignment->NameSize = NameSize;
- Assignment->Value = SwStringDuplicate(Value, ValueSize);
- if (Assignment->Value == NULL) {
- goto CreateAssignmentEnd;
- }
- Assignment->ValueSize = ValueSize;
- assert(Node->Type == ShellNodeSimpleCommand);
- INSERT_BEFORE(&(Assignment->ListEntry),
- &(Node->U.SimpleCommand.AssignmentList));
- Result = TRUE;
- CreateAssignmentEnd:
- if (Result == FALSE) {
- if (Assignment != NULL) {
- if (Assignment->Name != NULL) {
- free(Assignment->Name);
- }
- if (Assignment->Value != NULL) {
- free(Assignment->Value);
- }
- free(Assignment);
- }
- }
- return Result;
- }
- VOID
- ShDestroyAssignment (
- PSHELL_ASSIGNMENT Assignment
- )
- /*++
- Routine Description:
- This routine destroys an assignment entry.
- Arguments:
- Assignment - Supplies a pointer to the assignment.
- Return Value:
- None.
- --*/
- {
- LIST_REMOVE(&(Assignment->ListEntry));
- if (Assignment->Name != NULL) {
- free(Assignment->Name);
- }
- if (Assignment->Value != NULL) {
- free(Assignment->Value);
- }
- free(Assignment);
- return;
- }
- BOOL
- ShAddPatternToSet (
- PSHELL_CASE_PATTERN_SET Set,
- PSTR Pattern,
- UINTN PatternSize
- )
- /*++
- Routine Description:
- This routine adds a case pattern entry to the given case statement.
- Arguments:
- Set - Supplies a pointer to the set of patterns.
- Pattern - Supplies a pointer to the pattern string to add. A copy of this
- string will be made.
- PatternSize - Supplies the length of the pattern string in bytes including
- the null terminator.
- Return Value:
- TRUE on success.
- FALSE on failure.
- --*/
- {
- PSHELL_CASE_PATTERN_ENTRY Entry;
- Entry = malloc(sizeof(SHELL_CASE_PATTERN_ENTRY));
- if (Entry == NULL) {
- return FALSE;
- }
- Entry->Pattern = SwStringDuplicate(Pattern, PatternSize);
- if (Entry->Pattern == NULL) {
- free(Entry);
- return FALSE;
- }
- Entry->PatternSize = PatternSize;
- INSERT_BEFORE(&(Entry->ListEntry), &(Set->PatternEntryList));
- return TRUE;
- }
- VOID
- ShDestroyCasePatternList (
- PSHELL_NODE CaseNode
- )
- /*++
- Routine Description:
- This routine destroys the pattern sets in a case statement.
- Arguments:
- CaseNode - Supplies a pointer to the case statement.
- Return Value:
- None.
- --*/
- {
- PSHELL_CASE_PATTERN_ENTRY Entry;
- PSHELL_CASE_PATTERN_SET Set;
- assert(CaseNode->Type == ShellNodeCase);
- //
- // Loop through every set in the case.
- //
- while (LIST_EMPTY(&(CaseNode->U.Case.PatternList)) == FALSE) {
- Set = LIST_VALUE(CaseNode->U.Case.PatternList.Next,
- SHELL_CASE_PATTERN_SET,
- ListEntry);
- LIST_REMOVE(&(Set->ListEntry));
- if (Set->Action != NULL) {
- ShReleaseNode(Set->Action);
- }
- //
- // Loop through every entry in the set.
- //
- while (LIST_EMPTY(&(Set->PatternEntryList)) == FALSE) {
- Entry = LIST_VALUE(Set->PatternEntryList.Next,
- SHELL_CASE_PATTERN_ENTRY,
- ListEntry);
- LIST_REMOVE(&(Entry->ListEntry));
- free(Entry->Pattern);
- free(Entry);
- }
- free(Set);
- }
- return;
- }
- BOOL
- ShAddComponentToCommand (
- PSHELL_NODE Command,
- PSTR Component,
- UINTN ComponentSize
- )
- /*++
- Routine Description:
- This routine adds a component to a simple command string.
- Arguments:
- Command - Supplies a pointer to the simple command node.
- Component - Supplies a pointer to the component string, either the command
- or an argument.
- ComponentSize - Supplies the size of the component string in bytes
- including the null terminator.
- Return Value:
- TRUE on success.
- FALSE on allocation failure.
- --*/
- {
- BOOL Result;
- PSHELL_NODE_SIMPLE_COMMAND SimpleCommand;
- assert(Command->Type == ShellNodeSimpleCommand);
- assert(ComponentSize != 0);
- SimpleCommand = &(Command->U.SimpleCommand);
- Result = ShStringAppend(&(SimpleCommand->Arguments),
- &(SimpleCommand->ArgumentsSize),
- &(SimpleCommand->ArgumentsBufferCapacity),
- Component,
- ComponentSize);
- return Result;
- }
- BOOL
- ShIsStringQuoted (
- PSTR String
- )
- /*++
- Routine Description:
- This routine determines if any part of the given string is quoted, meaning
- it has a backslash, single quote, or double quote character in it.
- Arguments:
- String - Supplies a pointer to the string to check.
- Return Value:
- TRUE if the string has a quoting character in it.
- FALSE if the string is clean.
- --*/
- {
- while (*String != '\0') {
- if ((*String == SHELL_CONTROL_QUOTE) ||
- (*String == SHELL_CONTROL_ESCAPE)) {
- return TRUE;
- }
- String += 1;
- }
- return FALSE;
- }
- VOID
- ShParseError (
- PSHELL Shell,
- PSHELL_NODE Node,
- PSTR Format,
- ...
- )
- /*++
- Routine Description:
- This routine prints a shell parse error to standard error.
- Arguments:
- Shell - Supplies a pointer to the shell.
- Node - Supplies an optional pointer to the shell node being parsed.
- Format - Supplies the printf style format string.
- ... - Supplies the remaining arguments to the printf string.
- Return Value:
- TRUE if the string has a quoting character in it.
- FALSE if the string is clean.
- --*/
- {
- va_list ArgumentList;
- ULONG LineNumber;
- if (Node != NULL) {
- LineNumber = Node->LineNumber;
- } else {
- LineNumber = Shell->Lexer.LineNumber;
- }
- fprintf(stderr, "sh: %d: ", LineNumber);
- va_start(ArgumentList, Format);
- vfprintf(stderr, Format, ArgumentList);
- va_end(ArgumentList);
- if (Shell->Lexer.TokenBufferSize != 0) {
- //
- // Make sure the buffer is null terminated, ideally not clobbering the
- // valid part of the string.
- //
- if (Shell->Lexer.TokenBufferSize < Shell->Lexer.TokenBufferCapacity) {
- Shell->Lexer.TokenBuffer[Shell->Lexer.TokenBufferSize] = '\0';
- } else {
- Shell->Lexer.TokenBuffer[Shell->Lexer.TokenBufferSize - 1] = '\0';
- }
- fprintf(stderr, " Token: %s.\n", Shell->Lexer.TokenBuffer);
- } else {
- fprintf(stderr, "\n");
- }
- return;
- }
|