expand.c 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. expand.c
  5. Abstract:
  6. This module implements variable expansion for the shell.
  7. Author:
  8. Evan Green 10-Jun-2013
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "sh.h"
  16. #include "shparse.h"
  17. #include <assert.h>
  18. #include <ctype.h>
  19. #include <errno.h>
  20. #include <libgen.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include "../swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. //
  28. // Define the size of the string buffer needed to convert the argument count
  29. // integer to a string.
  30. //
  31. #define SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE 12
  32. #define SHELL_ARGUMENT_LENGTH_STRING_BUFFER_SIZE 12
  33. //
  34. // Define the maximum size of the options string.
  35. //
  36. #define SHELL_OPTION_STRING_SIZE 15
  37. //
  38. // Define the maximum size of a shell prompt expansion.
  39. //
  40. #define SHELL_PROMPT_EXPANSION_MAX 255
  41. //
  42. // Define the maximum size of the time format buffer.
  43. //
  44. #define SHELL_PROMPT_TIME_FORMAT_MAX 50
  45. //
  46. // ------------------------------------------------------ Data Type Definitions
  47. //
  48. typedef enum _SHELL_PARAMETER_MODIFIER {
  49. ShellParameterModifierInvalid,
  50. ShellParameterModifierNone,
  51. ShellParameterModifierLength,
  52. ShellParameterModifierUseDefault,
  53. ShellParameterModifierAssignDefault,
  54. ShellParameterModifierError,
  55. ShellParameterModifierAlternative,
  56. ShellParameterModifierRemoveSmallestSuffix,
  57. ShellParameterModifierRemoveLargestSuffix,
  58. ShellParameterModifierRemoveSmallestPrefix,
  59. ShellParameterModifierRemoveLargestPrefix,
  60. } SHELL_PARAMETER_MODIFIER, *PSHELL_PARAMETER_MODIFIER;
  61. //
  62. // ----------------------------------------------- Internal Function Prototypes
  63. //
  64. BOOL
  65. ShPerformExpansionsCore (
  66. PSHELL Shell,
  67. ULONG Options,
  68. PSTR *StringBufferAddress,
  69. PUINTN StringBufferSize,
  70. PUINTN StringBufferCapacity,
  71. PUINTN ExpansionIndex,
  72. PUINTN ExpansionEndIndex,
  73. PLIST_ENTRY ExpansionList
  74. );
  75. BOOL
  76. ShExpandNormalParameter (
  77. PSHELL Shell,
  78. BOOL Quoted,
  79. PSTR *StringBufferAddress,
  80. PUINTN StringBufferSize,
  81. PUINTN StringBufferCapacity,
  82. PUINTN ExpansionIndex,
  83. PUINTN ExpansionEndIndex,
  84. PLIST_ENTRY ExpansionList
  85. );
  86. BOOL
  87. ShExpandSpecialParameter (
  88. PSHELL Shell,
  89. BOOL Quoted,
  90. PSTR *StringBufferAddress,
  91. PUINTN StringBufferSize,
  92. PUINTN StringBufferCapacity,
  93. PUINTN ExpansionIndex,
  94. PUINTN ExpansionEndIndex,
  95. PLIST_ENTRY ExpansionList
  96. );
  97. BOOL
  98. ShExpandSubshell (
  99. PSHELL Shell,
  100. BOOL Quoted,
  101. PSTR *StringBufferAddress,
  102. PUINTN StringBufferSize,
  103. PUINTN StringBufferCapacity,
  104. PUINTN ExpansionIndex,
  105. PUINTN ExpansionEndIndex,
  106. PLIST_ENTRY ExpansionList
  107. );
  108. BOOL
  109. ShExpandTilde (
  110. PSHELL Shell,
  111. PSTR *StringBufferAddress,
  112. PUINTN StringBufferSize,
  113. PUINTN StringBufferCapacity,
  114. PUINTN ExpansionIndex,
  115. PUINTN ExpansionEndIndex,
  116. PLIST_ENTRY ExpansionList
  117. );
  118. BOOL
  119. ShExpandArithmeticExpression (
  120. PSHELL Shell,
  121. PSTR *StringBufferAddress,
  122. PUINTN StringBufferSize,
  123. PUINTN StringBufferCapacity,
  124. PUINTN ExpansionIndex,
  125. PUINTN ExpansionEndIndex,
  126. PLIST_ENTRY ExpansionList
  127. );
  128. BOOL
  129. ShCreateAllParametersString (
  130. PSHELL Shell,
  131. CHAR Separator,
  132. PSTR *NewString,
  133. PUINTN NewStringSize
  134. );
  135. BOOL
  136. ShCreateParameterCountString (
  137. PSHELL Shell,
  138. PSTR *NewString,
  139. PUINTN NewStringSize
  140. );
  141. BOOL
  142. ShCreateOptionsString (
  143. PSHELL Shell,
  144. PSTR *NewString,
  145. PUINTN NewStringSize
  146. );
  147. VOID
  148. ShGetPositionalArgument (
  149. PSHELL Shell,
  150. ULONG ArgumentNumber,
  151. PSTR *Argument,
  152. PUINTN ArgumentSize
  153. );
  154. BOOL
  155. ShAddExpansionRangeEntry (
  156. PLIST_ENTRY ListHead,
  157. SHELL_EXPANSION_TYPE Type,
  158. UINTN Index,
  159. UINTN Length
  160. );
  161. VOID
  162. ShTrimVariableValue (
  163. PSTR *Value,
  164. PUINTN ValueSize,
  165. PSTR Pattern,
  166. UINTN PatternSize,
  167. BOOL Prefix,
  168. BOOL Longest
  169. );
  170. BOOL
  171. ShEscapeSpecialCharacters (
  172. BOOL Quoted,
  173. PSTR *Value,
  174. PUINTN ValueSize
  175. );
  176. //
  177. // -------------------------------------------------------------------- Globals
  178. //
  179. //
  180. // ------------------------------------------------------------------ Functions
  181. //
  182. BOOL
  183. ShPerformExpansions (
  184. PSHELL Shell,
  185. PSTR String,
  186. UINTN StringSize,
  187. ULONG Options,
  188. PSTR *ExpandedString,
  189. PUINTN ExpandedStringSize,
  190. PSTR **Fields,
  191. PULONG FieldCount
  192. )
  193. /*++
  194. Routine Description:
  195. This routine performs expansion on a given string.
  196. Arguments:
  197. Shell - Supplies a pointer to the shell.
  198. String - Supplies a pointer to the string to expand.
  199. StringSize - Supplies a pointer to the size of the string in bytes
  200. including an assumed null terminator at the end.
  201. Options - Supplies the options for which expansions to perform. By default
  202. all expansions are performed, see SHELL_EXPANSION_OPTION_* for more
  203. details.
  204. ExpandedString - Supplies a pointer where a pointer to the expanded string
  205. will be returned on success. The caller is responsible for freeing this
  206. memory.
  207. ExpandedStringSize - Supplies a pointer where the size of the expanded
  208. string will be returned on success.
  209. Fields - Supplies a pointer where an array of pointers to strings will be
  210. returned representing the fields after field separator. This parameter
  211. is optional. The caller is responsible for freeing this memory.
  212. FieldCount - Supplies a pointer where the count of fields will be returned
  213. on success. This pointer is optional, but if the fields parameter is
  214. supplied this must be supplied as well.
  215. Return Value:
  216. TRUE on success.
  217. FALSE on failure.
  218. --*/
  219. {
  220. UINTN BufferCapacity;
  221. UINTN EndIndex;
  222. PSHELL_EXPANSION_RANGE Expansion;
  223. LIST_ENTRY ExpansionList;
  224. PSTR Field;
  225. ULONG FieldIndex;
  226. UINTN Index;
  227. BOOL Result;
  228. INITIALIZE_LIST_HEAD(&ExpansionList);
  229. if (Fields != NULL) {
  230. *Fields = NULL;
  231. }
  232. String = SwStringDuplicate(String, StringSize);
  233. if (String == NULL) {
  234. Result = FALSE;
  235. goto PerformExpansionsEnd;
  236. }
  237. //
  238. // Do most of the work of substituting the expansions and keeping a list of
  239. // them.
  240. //
  241. BufferCapacity = StringSize;
  242. Index = 0;
  243. EndIndex = StringSize;
  244. Result = ShPerformExpansionsCore(Shell,
  245. Options,
  246. &String,
  247. &StringSize,
  248. &BufferCapacity,
  249. &Index,
  250. &EndIndex,
  251. &ExpansionList);
  252. if (Result == FALSE) {
  253. goto PerformExpansionsEnd;
  254. }
  255. //
  256. // Perform field splitting and path expansion.
  257. //
  258. if ((Options & SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT) == 0) {
  259. assert((Fields != NULL) && (FieldCount != NULL));
  260. Result = ShFieldSplit(Shell,
  261. &String,
  262. &StringSize,
  263. &ExpansionList,
  264. 0,
  265. Fields,
  266. FieldCount);
  267. if (Result == FALSE) {
  268. goto PerformExpansionsEnd;
  269. }
  270. if (((Shell->Options & SHELL_OPTION_NO_PATHNAME_EXPANSION) == 0) &&
  271. ((Options & SHELL_EXPANSION_OPTION_NO_PATH_EXPANSION) == 0)) {
  272. Result = ShPerformPathExpansions(Shell,
  273. &String,
  274. &StringSize,
  275. Fields,
  276. FieldCount);
  277. if (Result == FALSE) {
  278. goto PerformExpansionsEnd;
  279. }
  280. }
  281. } else {
  282. assert((Fields == NULL) && (FieldCount == NULL));
  283. ShDeNullExpansions(Shell, String, StringSize);
  284. }
  285. //
  286. // Perform quote removal.
  287. //
  288. if ((Options & SHELL_EXPANSION_OPTION_NO_QUOTE_REMOVAL) == 0) {
  289. if (Fields == NULL) {
  290. ShStringDequote(String, StringSize, Options, &StringSize);
  291. } else {
  292. for (FieldIndex = 0; FieldIndex < *FieldCount; FieldIndex += 1) {
  293. Field = (*Fields)[FieldIndex];
  294. ShStringDequote(Field, strlen(Field) + 1, Options, NULL);
  295. }
  296. }
  297. }
  298. Result = TRUE;
  299. PerformExpansionsEnd:
  300. if (Result == FALSE) {
  301. if (String != NULL) {
  302. free(String);
  303. String = NULL;
  304. }
  305. if ((Fields != NULL) && (*Fields != NULL)) {
  306. free(*Fields);
  307. *FieldCount = 0;
  308. }
  309. StringSize = 0;
  310. }
  311. //
  312. // Free up the expansion list.
  313. //
  314. while (LIST_EMPTY(&ExpansionList) == FALSE) {
  315. Expansion = LIST_VALUE(ExpansionList.Next,
  316. SHELL_EXPANSION_RANGE,
  317. ListEntry);
  318. LIST_REMOVE(&(Expansion->ListEntry));
  319. free(Expansion);
  320. }
  321. *ExpandedString = String;
  322. *ExpandedStringSize = StringSize;
  323. return Result;
  324. }
  325. PLIST_ENTRY
  326. ShGetCurrentArgumentList (
  327. PSHELL Shell
  328. )
  329. /*++
  330. Routine Description:
  331. This routine returns active argument list, which is either the current
  332. function executing or the shell's list.
  333. Arguments:
  334. Shell - Supplies a pointer to the shell.
  335. Return Value:
  336. Returns a pointer to the active argument list.
  337. --*/
  338. {
  339. PLIST_ENTRY ArgumentList;
  340. PLIST_ENTRY CurrentEntry;
  341. PSHELL_EXECUTION_NODE ExecutionNode;
  342. ArgumentList = &(Shell->ArgumentList);
  343. //
  344. // If there's a function running, use that set of parameters, otherwise
  345. // use what the shell was invoked with.
  346. //
  347. CurrentEntry = Shell->ExecutionStack.Next;
  348. while (CurrentEntry != &(Shell->ExecutionStack)) {
  349. ExecutionNode = LIST_VALUE(CurrentEntry,
  350. SHELL_EXECUTION_NODE,
  351. ListEntry);
  352. CurrentEntry = CurrentEntry->Next;
  353. if (ExecutionNode->Node->Type == ShellNodeFunction) {
  354. ArgumentList = &(ExecutionNode->ArgumentList);
  355. break;
  356. }
  357. }
  358. return ArgumentList;
  359. }
  360. BOOL
  361. ShExpandPrompt (
  362. PSHELL Shell,
  363. PSTR String,
  364. UINTN StringSize,
  365. PSTR *ExpandedString,
  366. PUINTN ExpandedStringSize
  367. )
  368. /*++
  369. Routine Description:
  370. This routine performs special prompt expansions on the given value.
  371. Arguments:
  372. Shell - Supplies a pointer to the shell.
  373. String - Supplies a pointer to the string to expand.
  374. StringSize - Supplies a pointer to the size of the string in bytes
  375. including an assumed null terminator at the end.
  376. ExpandedString - Supplies a pointer where a pointer to the expanded string
  377. will be returned on success. The caller is responsible for freeing this
  378. memory.
  379. ExpandedStringSize - Supplies a pointer where the size of the expanded
  380. string will be returned on success.
  381. Return Value:
  382. TRUE on success.
  383. FALSE on failure.
  384. --*/
  385. {
  386. UINTN CharacterIndex;
  387. PSTR CurrentDirectory;
  388. UINTN CurrentDirectorySize;
  389. INT Difference;
  390. PSTR Expansion;
  391. CHAR ExpansionBuffer[SHELL_PROMPT_EXPANSION_MAX];
  392. INTN ExpansionSize;
  393. PSTR Home;
  394. UINTN HomeSize;
  395. UINTN Index;
  396. PSTR Period;
  397. UINTN RangeSize;
  398. BOOL Result;
  399. CHAR Specifier;
  400. INT Status;
  401. time_t Time;
  402. struct tm *TimeFields;
  403. PSTR TimeFormat;
  404. CHAR TimeFormatBuffer[SHELL_PROMPT_TIME_FORMAT_MAX];
  405. PSTR UserName;
  406. PSTR Working;
  407. UINTN WorkingCapacity;
  408. UINTN WorkingSize;
  409. UserName = NULL;
  410. WorkingSize = StringSize;
  411. WorkingCapacity = StringSize;
  412. Working = SwStringDuplicate(String, StringSize);
  413. if (Working == NULL) {
  414. Result = FALSE;
  415. goto ExpandPromptEnd;
  416. }
  417. Index = 0;
  418. //
  419. // Don't process the null terminator, or the very last character. Loop
  420. // processing all other characters.
  421. //
  422. while (Index + 2 < WorkingSize) {
  423. if (Working[Index] == '\\') {
  424. RangeSize = 2;
  425. Specifier = Working[Index + 1];
  426. Expansion = ExpansionBuffer;
  427. ExpansionSize = -1;
  428. //
  429. // \a is a bell character.
  430. //
  431. if (Specifier == 'a') {
  432. ExpansionBuffer[0] = '\a';
  433. ExpansionSize = 1;
  434. //
  435. // \e is an escape character.
  436. //
  437. } else if (Specifier == 'e') {
  438. ExpansionBuffer[0] = 0x1B;
  439. ExpansionSize = 1;
  440. //
  441. // \n is a newline.
  442. //
  443. } else if (Specifier == 'n') {
  444. ExpansionBuffer[0] = '\n';
  445. ExpansionSize = 1;
  446. //
  447. // \r is a carraige return.
  448. //
  449. } else if (Specifier == 'r') {
  450. ExpansionBuffer[0] = '\r';
  451. ExpansionSize = 1;
  452. //
  453. // \ is a literal backslash.
  454. //
  455. } else if (Specifier == '\\') {
  456. ExpansionBuffer[0] = SHELL_CONTROL_ESCAPE;
  457. ExpansionBuffer[1] = '\\';
  458. ExpansionSize = 2;
  459. //
  460. // \NNN is a character specified by the next one to three octal
  461. // characters.
  462. //
  463. } else if ((Specifier >= '0') && (Specifier <= '7')) {
  464. ExpansionBuffer[0] = 0;
  465. for (CharacterIndex = 0;
  466. CharacterIndex < 3;
  467. CharacterIndex += 1) {
  468. if (Index + 1 + CharacterIndex >= WorkingSize) {
  469. break;
  470. }
  471. Specifier = Working[Index + 1 + CharacterIndex];
  472. if (!((Specifier >= '0') && (Specifier <= '7'))) {
  473. break;
  474. }
  475. ExpansionBuffer[0] *= 8;
  476. ExpansionBuffer[0] += Specifier - '0';
  477. }
  478. RangeSize = CharacterIndex + 1;
  479. ExpansionSize = 1;
  480. //
  481. // \xNN is a character specified by the next one to two hexadecimal
  482. // digits.
  483. //
  484. } else if (Specifier == 'x') {
  485. ExpansionBuffer[0] = SHELL_CONTROL_ESCAPE;
  486. ExpansionBuffer[1] = 0;
  487. for (CharacterIndex = 0;
  488. CharacterIndex < 2;
  489. CharacterIndex += 1) {
  490. if (Index + 2 + CharacterIndex >= WorkingSize) {
  491. break;
  492. }
  493. Specifier = Working[Index + 2 + CharacterIndex];
  494. if (isdigit(Specifier)) {
  495. Specifier -= '0';
  496. } else if ((Specifier >= 'A') && (Specifier <= 'F')) {
  497. Specifier = Specifier - 'A' + 0xA;
  498. } else if ((Specifier >= 'a') && (Specifier <= 'f')) {
  499. Specifier = Specifier - 'a' + 0xA;
  500. } else {
  501. break;
  502. }
  503. ExpansionBuffer[1] *= 16;
  504. ExpansionBuffer[1] += Specifier;
  505. }
  506. RangeSize = CharacterIndex + 2;
  507. ExpansionSize = 2;
  508. //
  509. // $ comes out to # if the effective user ID is 0, or $ otherwise.
  510. // Since expansions haven't been performed yet, escape the
  511. // character.
  512. //
  513. } else if (Specifier == '$') {
  514. ExpansionBuffer[0] = SHELL_CONTROL_ESCAPE;
  515. ExpansionBuffer[1] = '$';
  516. ExpansionSize = 2;
  517. if (SwGetEffectiveUserId() == 0) {
  518. ExpansionBuffer[1] = '#';
  519. }
  520. //
  521. // w comes out to the current working directory, with $HOME
  522. // abbreviated with a tilde. W comes out to the same except only
  523. // the basename the directory.
  524. //
  525. } else if ((Specifier == 'w') || (Specifier == 'W')) {
  526. Result = ShGetVariable(Shell,
  527. SHELL_HOME,
  528. sizeof(SHELL_HOME),
  529. &Home,
  530. &HomeSize);
  531. if (Result == FALSE) {
  532. Home = NULL;
  533. HomeSize = 0;
  534. }
  535. Result = ShGetVariable(Shell,
  536. SHELL_PWD,
  537. sizeof(SHELL_PWD),
  538. &CurrentDirectory,
  539. &CurrentDirectorySize);
  540. if (Result != FALSE) {
  541. Difference = 1;
  542. //
  543. // Determine if home is a prefix of the current directory.
  544. //
  545. if ((HomeSize != 0) && (HomeSize <= CurrentDirectorySize)) {
  546. Difference = strncmp(Home,
  547. CurrentDirectory,
  548. HomeSize - 1);
  549. }
  550. //
  551. // If the user is at home, then it's just a ~ by itself.
  552. //
  553. if ((Difference == 0) &&
  554. (CurrentDirectorySize == HomeSize)) {
  555. ExpansionBuffer[0] = SHELL_CONTROL_ESCAPE;
  556. ExpansionBuffer[1] = '~';
  557. ExpansionSize = 2;
  558. //
  559. // If it's W, then the result is the basename.
  560. //
  561. } else if (Specifier == 'W') {
  562. Expansion = basename(CurrentDirectory);
  563. ExpansionSize = strlen(Expansion);
  564. //
  565. // The expansion is either the current directory directly,
  566. // or ~/remainder.
  567. //
  568. } else {
  569. if (Difference == 0) {
  570. ExpansionBuffer[0] = SHELL_CONTROL_ESCAPE;
  571. ExpansionBuffer[1] = '~';
  572. strncpy(ExpansionBuffer + 2,
  573. CurrentDirectory + HomeSize - 1,
  574. SHELL_PROMPT_EXPANSION_MAX - 2);
  575. ExpansionBuffer[SHELL_PROMPT_EXPANSION_MAX - 1] =
  576. '\0';
  577. ExpansionSize = strlen(ExpansionBuffer);
  578. } else {
  579. Expansion = CurrentDirectory;
  580. ExpansionSize = CurrentDirectorySize;
  581. if (ExpansionSize != 0) {
  582. ExpansionSize -= 1;
  583. }
  584. }
  585. }
  586. }
  587. //
  588. // h is the hostname up to the first period. H is the complete
  589. // hostname.
  590. //
  591. } else if ((Specifier == 'h') || (Specifier == 'H')) {
  592. Status = SwGetHostName(ExpansionBuffer,
  593. SHELL_PROMPT_EXPANSION_MAX);
  594. if (Status != 0) {
  595. ExpansionSize = 0;
  596. } else {
  597. if (Specifier == 'h') {
  598. Period = strchr(ExpansionBuffer, '.');
  599. if (Period != NULL) {
  600. *Period = '\0';
  601. }
  602. }
  603. ExpansionSize = strlen(ExpansionBuffer);
  604. }
  605. //
  606. // u is the username.
  607. //
  608. } else if (Specifier == 'u') {
  609. Status = SwGetUserNameFromId(SwGetEffectiveUserId(), &UserName);
  610. if ((Status == 0) && (UserName != NULL)) {
  611. Expansion = UserName;
  612. ExpansionSize = strlen(UserName);
  613. } else {
  614. ExpansionSize = 0;
  615. }
  616. //
  617. // [ and ] are used by bash to delineate control characters for
  618. // line counting purposes. This shell doesn't bother with that.
  619. //
  620. } else if ((Specifier == '[') || (Specifier == ']')) {
  621. ExpansionSize = 0;
  622. //
  623. // T is the current time in 12-hour HH:MM:SS format.
  624. // @ is the current time in 12-hour AM/PM format.
  625. // A is the current time in 24-hour HH:MM format.
  626. // t is the current time in 24-hour HH:MM:SS format.
  627. // d is the current date in weekday format: Tue Dec 9.
  628. // D is a custom format in {}.
  629. //
  630. } else if ((Specifier == 'T') || (Specifier == '@') ||
  631. (Specifier == 'A') || (Specifier == 't') ||
  632. (Specifier == 'd') || (Specifier == 'D')) {
  633. TimeFormat = "";
  634. if (Specifier == 'T') {
  635. TimeFormat = "%I:%M:%S";
  636. } else if (Specifier == '@') {
  637. TimeFormat = "%H:%M %p";
  638. } else if (Specifier == 'A') {
  639. TimeFormat = "%H:%M";
  640. } else if (Specifier == 't') {
  641. TimeFormat = "%H:%M:%S";
  642. } else if (Specifier == 'd') {
  643. TimeFormat = "%a %b %d";
  644. } else if (Specifier == 'D') {
  645. if ((Index + 2 <= WorkingSize) &&
  646. (Working[Index + 2] == '{')) {
  647. CharacterIndex = 0;
  648. while ((CharacterIndex + 1 <
  649. SHELL_PROMPT_TIME_FORMAT_MAX) &&
  650. ((Index + 3 + CharacterIndex) <
  651. WorkingSize)) {
  652. Specifier = Working[Index + 3 + CharacterIndex];
  653. if (Specifier == '}') {
  654. break;
  655. }
  656. TimeFormatBuffer[CharacterIndex] = Specifier;
  657. CharacterIndex += 1;
  658. }
  659. TimeFormatBuffer[CharacterIndex] = '\0';
  660. TimeFormat = TimeFormatBuffer;
  661. RangeSize = CharacterIndex + 4;
  662. }
  663. } else {
  664. assert(FALSE);
  665. }
  666. Time = time(NULL);
  667. TimeFields = localtime(&Time);
  668. ExpansionBuffer[0] = '\0';
  669. strftime(ExpansionBuffer,
  670. SHELL_PROMPT_EXPANSION_MAX,
  671. TimeFormat,
  672. TimeFields);
  673. ExpansionSize = strlen(ExpansionBuffer);
  674. //
  675. // ! prints the history number.
  676. // # prints the command number. Currently both are the same.
  677. //
  678. } else if ((Specifier == '!') || (Specifier == '#')) {
  679. snprintf(ExpansionBuffer,
  680. SHELL_PROMPT_EXPANSION_MAX,
  681. "%d",
  682. Shell->Lexer.LineNumber);
  683. ExpansionSize = strlen(ExpansionBuffer);
  684. //
  685. // L prints the currently executing line number.
  686. //
  687. } else if (Specifier == 'L') {
  688. snprintf(ExpansionBuffer,
  689. SHELL_PROMPT_EXPANSION_MAX,
  690. "%d",
  691. Shell->ExecutingLineNumber);
  692. ExpansionSize = strlen(ExpansionBuffer);
  693. //
  694. // j prints the current number of active jobs.
  695. //
  696. } else if (Specifier == 'j') {
  697. //
  698. // TODO: This should be the number of active jobs.
  699. //
  700. snprintf(ExpansionBuffer,
  701. SHELL_PROMPT_EXPANSION_MAX,
  702. "%d",
  703. 0);
  704. ExpansionSize = strlen(ExpansionBuffer);
  705. //
  706. // l prints the basename of the shell's terminal device.
  707. //
  708. } else if (Specifier == 'l') {
  709. ExpansionSize = 0;
  710. //
  711. // s prints the basename of $0 (the name of the shell).
  712. //
  713. } else if (Specifier == 's') {
  714. Expansion = basename(Shell->CommandName);
  715. ExpansionSize = strlen(Expansion);
  716. //
  717. // V prints the version, including revision number.
  718. //
  719. } else if (Specifier == 'V') {
  720. snprintf(ExpansionBuffer,
  721. SHELL_PROMPT_EXPANSION_MAX,
  722. "%d.%d.%d",
  723. SH_VERSION_MAJOR,
  724. SH_VERSION_MINOR,
  725. SwGetSerialVersion());
  726. ExpansionSize = strlen(ExpansionBuffer);
  727. //
  728. // v prints just the major and minor version numbers.
  729. //
  730. } else if (Specifier == 'v') {
  731. snprintf(ExpansionBuffer,
  732. SHELL_PROMPT_EXPANSION_MAX,
  733. "%d.%d",
  734. SH_VERSION_MAJOR,
  735. SH_VERSION_MINOR);
  736. ExpansionSize = strlen(ExpansionBuffer);
  737. //
  738. // This is an unrecognized expansion, just leave it as is.
  739. //
  740. } else {
  741. ExpansionSize = 0;
  742. RangeSize = 1;
  743. }
  744. //
  745. // Replace the string.
  746. //
  747. if (ExpansionSize != -1) {
  748. Result = SwStringReplaceRegion(&Working,
  749. &WorkingSize,
  750. &WorkingCapacity,
  751. Index,
  752. Index + RangeSize,
  753. Expansion,
  754. ExpansionSize + 1);
  755. if (Result == FALSE) {
  756. goto ExpandPromptEnd;
  757. }
  758. Index += ExpansionSize;
  759. } else {
  760. Index += RangeSize;
  761. }
  762. } else {
  763. Index += 1;
  764. }
  765. }
  766. Result = TRUE;
  767. ExpandPromptEnd:
  768. if (UserName != NULL) {
  769. free(UserName);
  770. }
  771. if (Result == FALSE) {
  772. if (Working != NULL) {
  773. free(Working);
  774. Working = NULL;
  775. }
  776. WorkingSize = 0;
  777. }
  778. *ExpandedString = Working;
  779. *ExpandedStringSize = WorkingSize;
  780. return Result;
  781. }
  782. //
  783. // --------------------------------------------------------- Internal Functions
  784. //
  785. BOOL
  786. ShPerformExpansionsCore (
  787. PSHELL Shell,
  788. ULONG Options,
  789. PSTR *StringBufferAddress,
  790. PUINTN StringBufferSize,
  791. PUINTN StringBufferCapacity,
  792. PUINTN ExpansionIndex,
  793. PUINTN ExpansionEndIndex,
  794. PLIST_ENTRY ExpansionList
  795. )
  796. /*++
  797. Routine Description:
  798. This routine performs expansion on a given string.
  799. Arguments:
  800. Shell - Supplies a pointer to the shell.
  801. Options - Supplies the options for which expansions to perform. By default
  802. all expansions are performed, see SHELL_EXPANSION_OPTION_* for more
  803. details.
  804. StringBufferAddress - Supplies a pointer to the address of the allocated
  805. string buffer. This value may be changed if the string is reallocated.
  806. StringBufferSize - Supplies a pointer that on input contains the size of
  807. the current string in bytes, including the null terminator. On output,
  808. it will contain the updated size of the string in bytes.
  809. StringBufferCapacity - Supplies a pointer that on input supplies the total
  810. size of the buffer. It will contain the updated size of the buffer
  811. allocation on output.
  812. ExpansionIndex - Supplies a pointer that on input contains the index of
  813. the first character of the expansion. On output, will contain the index
  814. immediately after the expansion.
  815. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  816. index to search for expansions. On output, this will be updated to
  817. reflect any expansions.
  818. ExpansionList - Supplies an optional pointer to the list of expansions
  819. that have occurred on this buffer. Any expansions here will be added
  820. to the end of this list.
  821. Return Value:
  822. TRUE on success.
  823. FALSE on failure.
  824. --*/
  825. {
  826. CHAR Character;
  827. CHAR NextCharacter;
  828. BOOL Quoted;
  829. BOOL Result;
  830. UINTN Start;
  831. BOOL TildeExpansion;
  832. BOOL ValidFirstName;
  833. Start = *ExpansionIndex;
  834. TildeExpansion = TRUE;
  835. if ((Options & SHELL_EXPANSION_OPTION_NO_TILDE_EXPANSION) != 0) {
  836. TildeExpansion = FALSE;
  837. }
  838. Quoted = FALSE;
  839. while (*ExpansionIndex < *ExpansionEndIndex) {
  840. Character = *(*StringBufferAddress + *ExpansionIndex);
  841. //
  842. // If it's an escape control character, skip this character and the
  843. // next one.
  844. //
  845. if (Character == SHELL_CONTROL_ESCAPE) {
  846. *ExpansionIndex += 2;
  847. assert(*ExpansionIndex <= *ExpansionEndIndex);
  848. continue;
  849. //
  850. // Remember whether or not this portion of the string is inside a
  851. // quoted region.
  852. //
  853. } else if (Character == SHELL_CONTROL_QUOTE) {
  854. Quoted = !Quoted;
  855. }
  856. //
  857. // Handle a dollar sign expansion.
  858. //
  859. if (Character == '$') {
  860. if (*ExpansionIndex + 1 < *ExpansionEndIndex) {
  861. NextCharacter = *(*StringBufferAddress + *ExpansionIndex + 1);
  862. ValidFirstName = SHELL_NAME_FIRST_CHARACTER(NextCharacter);
  863. //
  864. // If it was a digit or a special parameter, then it's a
  865. // parameter expansion.
  866. //
  867. if (SHELL_SPECIAL_PARAMETER_CHARACTER(NextCharacter)) {
  868. Result = ShExpandSpecialParameter(Shell,
  869. Quoted,
  870. StringBufferAddress,
  871. StringBufferSize,
  872. StringBufferCapacity,
  873. ExpansionIndex,
  874. ExpansionEndIndex,
  875. ExpansionList);
  876. if (Result == FALSE) {
  877. goto PerformExpansionsCoreEnd;
  878. }
  879. //
  880. // A single curly or a valid first name character is a
  881. // parameter expansion.
  882. //
  883. } else if ((ValidFirstName != FALSE) ||
  884. (NextCharacter == '{')) {
  885. Result = ShExpandNormalParameter(Shell,
  886. Quoted,
  887. StringBufferAddress,
  888. StringBufferSize,
  889. StringBufferCapacity,
  890. ExpansionIndex,
  891. ExpansionEndIndex,
  892. ExpansionList);
  893. if (Result == FALSE) {
  894. goto PerformExpansionsCoreEnd;
  895. }
  896. //
  897. // Note if it's a single parentheses. It could also be a double
  898. // parentheses, which would be arithmetic expansion.
  899. //
  900. } else if (NextCharacter == '(') {
  901. if (*ExpansionIndex + 2 < *ExpansionEndIndex) {
  902. NextCharacter =
  903. *(*StringBufferAddress + *ExpansionIndex + 2);
  904. if (NextCharacter == '(') {
  905. Result = ShExpandArithmeticExpression(
  906. Shell,
  907. StringBufferAddress,
  908. StringBufferSize,
  909. StringBufferCapacity,
  910. ExpansionIndex,
  911. ExpansionEndIndex,
  912. ExpansionList);
  913. } else {
  914. Result = ShExpandSubshell(Shell,
  915. Quoted,
  916. StringBufferAddress,
  917. StringBufferSize,
  918. StringBufferCapacity,
  919. ExpansionIndex,
  920. ExpansionEndIndex,
  921. ExpansionList);
  922. }
  923. if (Result == FALSE) {
  924. goto PerformExpansionsCoreEnd;
  925. }
  926. }
  927. } else {
  928. *ExpansionIndex += 1;
  929. }
  930. } else {
  931. *ExpansionIndex += 1;
  932. }
  933. //
  934. // If this is an unquoted tilde then it's the beginning of tilde
  935. // expansion. Tildes are only expanded at the start of the expansion or
  936. // right after a space.
  937. //
  938. } else if ((Character == '~') && (TildeExpansion != FALSE) &&
  939. ((*ExpansionIndex == Start) ||
  940. (isspace(*(*StringBufferAddress + *ExpansionIndex - 1))))) {
  941. assert(Quoted == FALSE);
  942. Result = ShExpandTilde(Shell,
  943. StringBufferAddress,
  944. StringBufferSize,
  945. StringBufferCapacity,
  946. ExpansionIndex,
  947. ExpansionEndIndex,
  948. ExpansionList);
  949. if (Result == FALSE) {
  950. goto PerformExpansionsCoreEnd;
  951. }
  952. //
  953. // If this is an unquoted backquote then it's the beginning of command
  954. // substitution.
  955. //
  956. } else if (Character == '`') {
  957. Result = ShExpandSubshell(Shell,
  958. Quoted,
  959. StringBufferAddress,
  960. StringBufferSize,
  961. StringBufferCapacity,
  962. ExpansionIndex,
  963. ExpansionEndIndex,
  964. ExpansionList);
  965. if (Result == FALSE) {
  966. goto PerformExpansionsCoreEnd;
  967. }
  968. //
  969. // No expansion, just move to the next character.
  970. //
  971. } else {
  972. *ExpansionIndex += 1;
  973. }
  974. }
  975. Result = TRUE;
  976. PerformExpansionsCoreEnd:
  977. return Result;
  978. }
  979. BOOL
  980. ShExpandNormalParameter (
  981. PSHELL Shell,
  982. BOOL Quoted,
  983. PSTR *StringBufferAddress,
  984. PUINTN StringBufferSize,
  985. PUINTN StringBufferCapacity,
  986. PUINTN ExpansionIndex,
  987. PUINTN ExpansionEndIndex,
  988. PLIST_ENTRY ExpansionList
  989. )
  990. /*++
  991. Routine Description:
  992. This routine performs parameter substitution.
  993. Arguments:
  994. Shell - Supplies a pointer to the shell.
  995. Quoted - Supplies a boolean indicating if the expansion is happening
  996. underneath double quotes or not.
  997. StringBufferAddress - Supplies a pointer to the address of the allocated
  998. string buffer. This value may be changed if the string is reallocated.
  999. StringBufferSize - Supplies a pointer that on input contains the size of
  1000. the current string in bytes, including the null terminator. On output,
  1001. it will contain the updated size of the string in bytes.
  1002. StringBufferCapacity - Supplies a pointer that on input supplies the total
  1003. size of the buffer. It will contain the updated size of the buffer
  1004. allocation on output.
  1005. ExpansionIndex - Supplies a pointer that on input contains the index of
  1006. the first character of the expansion. On output, will contain the index
  1007. immediately after the expansion.
  1008. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  1009. index to search for expansions. On output, this will be updated to
  1010. reflect any expansions.
  1011. ExpansionList - Supplies an optional pointer to the list of expansions
  1012. that have occurred on this buffer. Any expansions here will be added
  1013. to the end of this list.
  1014. Return Value:
  1015. TRUE on success.
  1016. FALSE on failure.
  1017. --*/
  1018. {
  1019. PSTR AllocatedValue;
  1020. UINTN ArgumentEnd;
  1021. BOOL Curly;
  1022. PLIST_ENTRY CurrentEntry;
  1023. UINTN CurrentIndex;
  1024. UINTN Delta;
  1025. BOOL EndModifierFound;
  1026. PSHELL_EXPANSION_RANGE Expansion;
  1027. UINTN ExpansionInnerBegin;
  1028. UINTN ExpansionInnerEnd;
  1029. UINTN ExpansionOuterBegin;
  1030. UINTN ExpansionOuterEnd;
  1031. SHELL_EXPANSION_TYPE ExpansionType;
  1032. CHAR FirstCharacter;
  1033. UINTN Length;
  1034. CHAR LengthBuffer[SHELL_ARGUMENT_LENGTH_STRING_BUFFER_SIZE];
  1035. SHELL_PARAMETER_MODIFIER Modifier;
  1036. UINTN ModifierBegin;
  1037. LIST_ENTRY ModifierExpansionList;
  1038. PSTR ModifierWord;
  1039. UINTN ModifierWordSize;
  1040. BOOL NullIsUnset;
  1041. ULONG Options;
  1042. UINTN OriginalEnd;
  1043. LONG ParameterNumber;
  1044. BOOL PropagateExpansions;
  1045. BOOL Result;
  1046. PSTR String;
  1047. BOOL UsedModifier;
  1048. PSTR Value;
  1049. UINTN ValueCapacity;
  1050. UINTN ValueSize;
  1051. PSTR VariableName;
  1052. PSTR VariableNameCopy;
  1053. UINTN VariableNameSize;
  1054. AllocatedValue = NULL;
  1055. ExpansionOuterBegin = *ExpansionIndex;
  1056. ExpansionType = ShellExpansionFieldSplit;
  1057. Modifier = ShellParameterModifierNone;
  1058. INITIALIZE_LIST_HEAD(&ModifierExpansionList);
  1059. ModifierWord = NULL;
  1060. ModifierWordSize = 0;
  1061. ModifierBegin = 0;
  1062. NullIsUnset = FALSE;
  1063. ParameterNumber = -1;
  1064. PropagateExpansions = FALSE;
  1065. String = *StringBufferAddress;
  1066. UsedModifier = FALSE;
  1067. Value = NULL;
  1068. ValueSize = 0;
  1069. VariableName = NULL;
  1070. VariableNameSize = 0;
  1071. assert(ExpansionOuterBegin < *StringBufferSize);
  1072. assert(String[ExpansionOuterBegin] == '$');
  1073. CurrentIndex = ExpansionOuterBegin + 1;
  1074. ExpansionInnerBegin = CurrentIndex;
  1075. if (CurrentIndex >= *StringBufferSize) {
  1076. Result = FALSE;
  1077. goto ExpandNormalParameterEnd;
  1078. }
  1079. Result = ShScanPastExpansion(String + ExpansionOuterBegin,
  1080. *StringBufferSize - ExpansionOuterBegin,
  1081. &ExpansionOuterEnd);
  1082. if (Result == FALSE) {
  1083. goto ExpandNormalParameterEnd;
  1084. }
  1085. ExpansionOuterEnd += ExpansionOuterBegin;
  1086. //
  1087. // Remember if there's a curly at the beginning.
  1088. //
  1089. assert((String[CurrentIndex] == '{') ||
  1090. (SHELL_NAME_FIRST_CHARACTER(String[CurrentIndex]) != FALSE));
  1091. Curly = FALSE;
  1092. if (String[CurrentIndex] == '{') {
  1093. Curly = TRUE;
  1094. ExpansionInnerBegin += 1;
  1095. //
  1096. // If there's a pound sign right after the curly, then it's actually
  1097. // a request for the length of this expansion. But watch out for ${#}
  1098. // on its own.
  1099. //
  1100. CurrentIndex += 1;
  1101. if ((CurrentIndex + 1 < *StringBufferSize) &&
  1102. (String[CurrentIndex] == '#') &&
  1103. (String[CurrentIndex + 1] != '}')) {
  1104. Modifier = ShellParameterModifierLength;
  1105. ExpansionInnerBegin += 1;
  1106. CurrentIndex += 1;
  1107. }
  1108. }
  1109. //
  1110. // Get the span of the name.
  1111. //
  1112. ExpansionInnerEnd = CurrentIndex;
  1113. if ((CurrentIndex < *StringBufferSize) &&
  1114. (SHELL_SPECIAL_PARAMETER_CHARACTER(String[CurrentIndex]))) {
  1115. CurrentIndex += 1;
  1116. ExpansionInnerEnd = CurrentIndex;
  1117. } else {
  1118. while (CurrentIndex < *StringBufferSize) {
  1119. if ((SHELL_NAME_CHARACTER(String[CurrentIndex]) == FALSE) ||
  1120. (String[CurrentIndex] == '#')) {
  1121. break;
  1122. }
  1123. CurrentIndex += 1;
  1124. ExpansionInnerEnd = CurrentIndex;
  1125. }
  1126. }
  1127. if (CurrentIndex == *StringBufferSize) {
  1128. Result = FALSE;
  1129. goto ExpandNormalParameterEnd;
  1130. }
  1131. if (ExpansionInnerBegin == ExpansionInnerEnd) {
  1132. Result = FALSE;
  1133. goto ExpandNormalParameterEnd;
  1134. }
  1135. //
  1136. // Look for modifiers if this is in a curly.
  1137. //
  1138. if (Curly != FALSE) {
  1139. //
  1140. // If there's an optional colon, then null is the same thing as unset
  1141. // to other modifiers.
  1142. //
  1143. if (String[CurrentIndex] == ':') {
  1144. NullIsUnset = TRUE;
  1145. CurrentIndex += 1;
  1146. if (CurrentIndex == *StringBufferSize) {
  1147. Result = FALSE;
  1148. goto ExpandNormalParameterEnd;
  1149. }
  1150. }
  1151. //
  1152. // Look for other modifiers.
  1153. //
  1154. EndModifierFound = TRUE;
  1155. if (String[CurrentIndex] == '-') {
  1156. PropagateExpansions = TRUE;
  1157. Modifier = ShellParameterModifierUseDefault;
  1158. } else if (String[CurrentIndex] == '=') {
  1159. Modifier = ShellParameterModifierAssignDefault;
  1160. } else if (String[CurrentIndex] == '?') {
  1161. Modifier = ShellParameterModifierError;
  1162. } else if (String[CurrentIndex] == '+') {
  1163. PropagateExpansions = TRUE;
  1164. Modifier = ShellParameterModifierAlternative;
  1165. } else if (String[CurrentIndex] == '%') {
  1166. Modifier = ShellParameterModifierRemoveSmallestSuffix;
  1167. if ((CurrentIndex + 1 < *StringBufferSize) &&
  1168. (String[CurrentIndex + 1] == '%')) {
  1169. Modifier = ShellParameterModifierRemoveLargestSuffix;
  1170. CurrentIndex += 1;
  1171. }
  1172. } else if (String[CurrentIndex] == '#') {
  1173. Modifier = ShellParameterModifierRemoveSmallestPrefix;
  1174. if ((CurrentIndex + 1 < *StringBufferSize) &&
  1175. (String[CurrentIndex + 1] == '#')) {
  1176. Modifier = ShellParameterModifierRemoveLargestPrefix;
  1177. CurrentIndex += 1;
  1178. }
  1179. } else {
  1180. EndModifierFound = FALSE;
  1181. }
  1182. //
  1183. // If a modifier was found on the end, advance the string past that
  1184. // character.
  1185. //
  1186. if (EndModifierFound != FALSE) {
  1187. CurrentIndex += 1;
  1188. if (CurrentIndex >= *StringBufferSize) {
  1189. Result = FALSE;
  1190. goto ExpandNormalParameterEnd;
  1191. }
  1192. }
  1193. ArgumentEnd = ExpansionOuterEnd - 1;
  1194. //
  1195. // If there is an argument, expand it in place. Put the expansions on a
  1196. // separate modifier list which will get merged onto the main list if
  1197. // the modifier is chosen. The expansion list entries are important
  1198. // because something like ${1+"$@"} does indeed split into multiple
  1199. // fields.
  1200. //
  1201. Options = SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT;
  1202. if (ArgumentEnd != CurrentIndex) {
  1203. ModifierBegin = CurrentIndex;
  1204. OriginalEnd = ArgumentEnd;
  1205. Result = ShPerformExpansionsCore(Shell,
  1206. Options,
  1207. StringBufferAddress,
  1208. StringBufferSize,
  1209. StringBufferCapacity,
  1210. &CurrentIndex,
  1211. &ArgumentEnd,
  1212. &ModifierExpansionList);
  1213. if (Result == FALSE) {
  1214. goto ExpandNormalParameterEnd;
  1215. }
  1216. String = *StringBufferAddress;
  1217. *ExpansionEndIndex += ArgumentEnd - OriginalEnd;
  1218. ExpansionOuterEnd += ArgumentEnd - OriginalEnd;
  1219. //
  1220. // Copy the modifier word.
  1221. //
  1222. ModifierWordSize = ArgumentEnd - ModifierBegin + 1;
  1223. ModifierWord = SwStringDuplicate(String + ModifierBegin,
  1224. ModifierWordSize);
  1225. if (ModifierWord == NULL) {
  1226. Result = FALSE;
  1227. goto ExpandNormalParameterEnd;
  1228. }
  1229. //
  1230. // Dequote the word if it's being used for pattern matching or
  1231. // assignment, but not for the "use if (not) set".
  1232. //
  1233. if ((PropagateExpansions == FALSE) || (Quoted != FALSE)) {
  1234. ShStringDequote(ModifierWord,
  1235. ModifierWordSize,
  1236. 0,
  1237. &ModifierWordSize);
  1238. }
  1239. if (PropagateExpansions == FALSE) {
  1240. ShDeNullExpansions(Shell, ModifierWord, ModifierWordSize);
  1241. }
  1242. }
  1243. String = *StringBufferAddress;
  1244. CurrentIndex = ArgumentEnd + 1;
  1245. }
  1246. //
  1247. // If the first character was a digit, then this is a positional parameter.
  1248. // Otherwise, it's a regular variable name.
  1249. //
  1250. FirstCharacter = String[ExpansionInnerBegin];
  1251. if ((FirstCharacter >= '0') && (FirstCharacter <= '9')) {
  1252. ParameterNumber = strtol(String + ExpansionInnerBegin, NULL, 10);
  1253. if (ParameterNumber < 0) {
  1254. return FALSE;
  1255. }
  1256. ShGetPositionalArgument(Shell, ParameterNumber, &Value, &ValueSize);
  1257. //
  1258. // If the first character was a special parameter, expand that. Tell the
  1259. // special parameter expansion that no escaping is necessary because that's
  1260. // going to be done here.
  1261. //
  1262. } else if (SHELL_SPECIAL_PARAMETER_CHARACTER(FirstCharacter)) {
  1263. ValueSize = sizeof("$$");
  1264. Value = malloc(ValueSize);
  1265. if (Value == NULL) {
  1266. Result = FALSE;
  1267. goto ExpandNormalParameterEnd;
  1268. }
  1269. snprintf(Value, ValueSize, "$%c", FirstCharacter);
  1270. ValueCapacity = ValueSize;
  1271. Result = ShExpandSpecialParameter(Shell,
  1272. FALSE,
  1273. &Value,
  1274. &ValueSize,
  1275. &ValueCapacity,
  1276. NULL,
  1277. NULL,
  1278. NULL);
  1279. AllocatedValue = Value;
  1280. if (Result == FALSE) {
  1281. goto ExpandNormalParameterEnd;
  1282. }
  1283. if (FirstCharacter == '@') {
  1284. ExpansionType = ShellExpansionSplitOnNull;
  1285. }
  1286. } else {
  1287. VariableName = *StringBufferAddress + ExpansionInnerBegin;
  1288. VariableNameSize = ExpansionInnerEnd - ExpansionInnerBegin + 1;
  1289. ShGetVariable(Shell,
  1290. VariableName,
  1291. VariableNameSize,
  1292. &Value,
  1293. &ValueSize);
  1294. }
  1295. //
  1296. // Run the value through any modifiers.
  1297. //
  1298. switch (Modifier) {
  1299. case ShellParameterModifierNone:
  1300. break;
  1301. case ShellParameterModifierLength:
  1302. Length = 0;
  1303. if (Value != NULL) {
  1304. Length = strlen(Value);
  1305. }
  1306. ValueSize = snprintf(LengthBuffer,
  1307. SHELL_ARGUMENT_LENGTH_STRING_BUFFER_SIZE,
  1308. "%d",
  1309. (LONG)Length) + 1;
  1310. Value = LengthBuffer;
  1311. if (AllocatedValue != NULL) {
  1312. free(AllocatedValue);
  1313. AllocatedValue = NULL;
  1314. }
  1315. break;
  1316. case ShellParameterModifierUseDefault:
  1317. if ((Value == NULL) || ((NullIsUnset != FALSE) && (ValueSize == 1))) {
  1318. Value = ModifierWord;
  1319. ValueSize = ModifierWordSize;
  1320. }
  1321. break;
  1322. case ShellParameterModifierAssignDefault:
  1323. if ((Value == NULL) || ((NullIsUnset != FALSE) && (ValueSize == 1))) {
  1324. //
  1325. // Only real variable names can be set with assignment.
  1326. //
  1327. if (VariableName == NULL) {
  1328. Result = FALSE;
  1329. goto ExpandNormalParameterEnd;
  1330. }
  1331. Result = ShSetVariable(Shell,
  1332. VariableName,
  1333. VariableNameSize,
  1334. ModifierWord,
  1335. ModifierWordSize);
  1336. if (Result == FALSE) {
  1337. goto ExpandNormalParameterEnd;
  1338. }
  1339. Value = ModifierWord;
  1340. ValueSize = ModifierWordSize;
  1341. }
  1342. break;
  1343. case ShellParameterModifierError:
  1344. if ((Value == NULL) || ((NullIsUnset != FALSE) && (ValueSize == 1))) {
  1345. if (VariableName != NULL) {
  1346. VariableNameCopy = SwStringDuplicate(VariableName,
  1347. VariableNameSize);
  1348. printf("%s: %s\n", VariableNameCopy, ModifierWord);
  1349. if (VariableNameCopy != NULL) {
  1350. free(VariableNameCopy);
  1351. }
  1352. } else {
  1353. assert(ParameterNumber >= 0);
  1354. printf("%d: %s\n", ParameterNumber, ModifierWord);
  1355. }
  1356. Result = FALSE;
  1357. goto ExpandNormalParameterEnd;
  1358. }
  1359. break;
  1360. case ShellParameterModifierAlternative:
  1361. if (!((Value == NULL) ||
  1362. ((NullIsUnset != FALSE) && (ValueSize == 1)))) {
  1363. Value = ModifierWord;
  1364. ValueSize = ModifierWordSize;
  1365. }
  1366. break;
  1367. case ShellParameterModifierRemoveSmallestSuffix:
  1368. ShTrimVariableValue(&Value,
  1369. &ValueSize,
  1370. ModifierWord,
  1371. ModifierWordSize,
  1372. FALSE,
  1373. FALSE);
  1374. break;
  1375. case ShellParameterModifierRemoveLargestSuffix:
  1376. ShTrimVariableValue(&Value,
  1377. &ValueSize,
  1378. ModifierWord,
  1379. ModifierWordSize,
  1380. FALSE,
  1381. TRUE);
  1382. break;
  1383. case ShellParameterModifierRemoveSmallestPrefix:
  1384. ShTrimVariableValue(&Value,
  1385. &ValueSize,
  1386. ModifierWord,
  1387. ModifierWordSize,
  1388. TRUE,
  1389. FALSE);
  1390. break;
  1391. case ShellParameterModifierRemoveLargestPrefix:
  1392. ShTrimVariableValue(&Value,
  1393. &ValueSize,
  1394. ModifierWord,
  1395. ModifierWordSize,
  1396. TRUE,
  1397. TRUE);
  1398. break;
  1399. default:
  1400. assert(FALSE);
  1401. return FALSE;
  1402. }
  1403. if (Value == ModifierWord) {
  1404. UsedModifier = TRUE;
  1405. }
  1406. //
  1407. // Ensure the value is heap allocated.
  1408. //
  1409. if ((Value != AllocatedValue) && (Value != ModifierWord)) {
  1410. assert(AllocatedValue == NULL);
  1411. Value = SwStringDuplicate(Value, ValueSize);
  1412. if (Value == NULL) {
  1413. Result = FALSE;
  1414. goto ExpandNormalParameterEnd;
  1415. }
  1416. }
  1417. //
  1418. // Careful with the heap management here. The escape special characters
  1419. // function may free the value and allocate something different.
  1420. //
  1421. if (Value == ModifierWord) {
  1422. ModifierWord = NULL;
  1423. }
  1424. assert((AllocatedValue == NULL) || (AllocatedValue == Value));
  1425. //
  1426. // Don't escape character if it's an unquoted modifier word, as quote
  1427. // removal was never performed on the original modifier.
  1428. //
  1429. if ((UsedModifier == FALSE) || (Quoted != FALSE)) {
  1430. Result = ShEscapeSpecialCharacters(Quoted, &Value, &ValueSize);
  1431. }
  1432. AllocatedValue = Value;
  1433. if (Result == FALSE) {
  1434. goto ExpandNormalParameterEnd;
  1435. }
  1436. //
  1437. // Replace the expansion region with the final value.
  1438. //
  1439. Result = SwStringReplaceRegion(StringBufferAddress,
  1440. StringBufferSize,
  1441. StringBufferCapacity,
  1442. ExpansionOuterBegin,
  1443. ExpansionOuterEnd,
  1444. Value,
  1445. ValueSize);
  1446. if (Result == FALSE) {
  1447. return FALSE;
  1448. }
  1449. if (ValueSize != 0) {
  1450. ValueSize -= 1;
  1451. }
  1452. //
  1453. // Either use the expansions from the modifier, or create one.
  1454. //
  1455. if ((UsedModifier != FALSE) && (PropagateExpansions != FALSE)) {
  1456. while (LIST_EMPTY(&ModifierExpansionList) == FALSE) {
  1457. CurrentEntry = ModifierExpansionList.Next;
  1458. LIST_REMOVE(CurrentEntry);
  1459. Expansion = LIST_VALUE(CurrentEntry,
  1460. SHELL_EXPANSION_RANGE,
  1461. ListEntry);
  1462. //
  1463. // Shift the expansion down. For the expansion ${x+...}, the
  1464. // expanded range ... needs to be shifted down by "${x+".
  1465. //
  1466. assert(ModifierBegin > ExpansionOuterBegin);
  1467. Expansion->Index -= ModifierBegin - ExpansionOuterBegin;
  1468. INSERT_BEFORE(CurrentEntry, ExpansionList);
  1469. }
  1470. } else {
  1471. Result = ShAddExpansionRangeEntry(ExpansionList,
  1472. ExpansionType,
  1473. ExpansionOuterBegin,
  1474. ValueSize);
  1475. if (Result == FALSE) {
  1476. return FALSE;
  1477. }
  1478. }
  1479. *ExpansionIndex += ValueSize;
  1480. Delta = ValueSize - (ExpansionOuterEnd - ExpansionOuterBegin);
  1481. *ExpansionEndIndex += Delta;
  1482. ExpandNormalParameterEnd:
  1483. if (ModifierWord != NULL) {
  1484. free(ModifierWord);
  1485. }
  1486. if (AllocatedValue != NULL) {
  1487. free(AllocatedValue);
  1488. }
  1489. while (LIST_EMPTY(&ModifierExpansionList) == FALSE) {
  1490. CurrentEntry = ModifierExpansionList.Next;
  1491. LIST_REMOVE(CurrentEntry);
  1492. Expansion = LIST_VALUE(CurrentEntry, SHELL_EXPANSION_RANGE, ListEntry);
  1493. free(Expansion);
  1494. }
  1495. return Result;
  1496. }
  1497. BOOL
  1498. ShExpandSpecialParameter (
  1499. PSHELL Shell,
  1500. BOOL Quoted,
  1501. PSTR *StringBufferAddress,
  1502. PUINTN StringBufferSize,
  1503. PUINTN StringBufferCapacity,
  1504. PUINTN ExpansionIndex,
  1505. PUINTN ExpansionEndIndex,
  1506. PLIST_ENTRY ExpansionList
  1507. )
  1508. /*++
  1509. Routine Description:
  1510. This routine performs parameter substitution for a special parameter.
  1511. Arguments:
  1512. Shell - Supplies a pointer to the shell.
  1513. Quoted - Supplies a boolean indicating if the expansion is happening
  1514. underneath double quotes or not.
  1515. StringBufferAddress - Supplies a pointer to the address of the allocated
  1516. string buffer. This value may be changed if the string is reallocated.
  1517. StringBufferSize - Supplies a pointer that on input contains the size of
  1518. the current string in bytes, including the null terminator. On output,
  1519. it will contain the updated size of the string in bytes.
  1520. StringBufferCapacity - Supplies a pointer that on input supplies the total
  1521. size of the buffer. It will contain the updated size of the buffer
  1522. allocation on output.
  1523. ExpansionIndex - Supplies a pointer that on input contains the index of
  1524. the first character of the expansion. On output, will contain the index
  1525. immediately after the expansion.
  1526. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  1527. index to search for expansions. On output, this will be updated to
  1528. reflect any expansions.
  1529. ExpansionList - Supplies an optional pointer to the list of expansions
  1530. that have occurred on this buffer. Any expansions here will be added
  1531. to the end of this list.
  1532. Return Value:
  1533. TRUE on success.
  1534. FALSE on failure.
  1535. --*/
  1536. {
  1537. PSTR AllocatedValue;
  1538. UINTN Delta;
  1539. UINTN ExpansionOuterBegin;
  1540. UINTN ExpansionOuterEnd;
  1541. CHAR LocalBuffer[SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE];
  1542. BOOL Result;
  1543. CHAR SpecialCharacter;
  1544. PSTR String;
  1545. SHELL_EXPANSION_TYPE Type;
  1546. PSTR Value;
  1547. UINTN ValueSize;
  1548. AllocatedValue = NULL;
  1549. Result = FALSE;
  1550. Type = ShellExpansionFieldSplit;
  1551. Value = NULL;
  1552. ValueSize = 0;
  1553. String = *StringBufferAddress;
  1554. ExpansionOuterBegin = 0;
  1555. if (ExpansionIndex != NULL) {
  1556. ExpansionOuterBegin = *ExpansionIndex;
  1557. }
  1558. ExpansionOuterEnd = ExpansionOuterBegin + 2;
  1559. assert(ExpansionOuterEnd <= *StringBufferSize);
  1560. assert(String[ExpansionOuterBegin] == '$');
  1561. SpecialCharacter = String[ExpansionOuterBegin + 1];
  1562. if ((SpecialCharacter >= '0') && (SpecialCharacter <= '9')) {
  1563. ShGetPositionalArgument(Shell,
  1564. SpecialCharacter - '0',
  1565. &Value,
  1566. &ValueSize);
  1567. Result = TRUE;
  1568. } else {
  1569. switch (SpecialCharacter) {
  1570. //
  1571. // The @ character expands to all positional parameters starting from
  1572. // one. This is the only parameter that will split into separate fields
  1573. // even if inside a double quote.
  1574. //
  1575. case '@':
  1576. Type = ShellExpansionSplitOnNull;
  1577. Result = ShCreateAllParametersString(Shell,
  1578. '\0',
  1579. &Value,
  1580. &ValueSize);
  1581. AllocatedValue = Value;
  1582. break;
  1583. //
  1584. // The * parameter expands to all positional parameters starting from
  1585. // one If inside double quotes, it will expand all to one field.
  1586. //
  1587. case '*':
  1588. Type = ShellExpansionFieldSplit;
  1589. Result = ShCreateAllParametersString(Shell,
  1590. ' ',
  1591. &Value,
  1592. &ValueSize);
  1593. AllocatedValue = Value;
  1594. break;
  1595. //
  1596. // The # character expands to the decimal number of parameters, not
  1597. // counting 0 (the command name).
  1598. //
  1599. case '#':
  1600. Result = ShCreateParameterCountString(Shell, &Value, &ValueSize);
  1601. AllocatedValue = Value;
  1602. break;
  1603. //
  1604. // The ? character expands to the decimal exit status of the most
  1605. // recent pipeline.
  1606. //
  1607. case '?':
  1608. ValueSize = snprintf(LocalBuffer,
  1609. SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE,
  1610. "%d",
  1611. Shell->LastReturnValue);
  1612. if (ValueSize == -1) {
  1613. goto ExpandSpecialParameterEnd;
  1614. }
  1615. ValueSize += 1;
  1616. Value = LocalBuffer;
  1617. Result = TRUE;
  1618. break;
  1619. //
  1620. // The hyphen character expands to the current option flags (the
  1621. // single letter option names concatenated into a string) as specified
  1622. // on invocation, by the set command, or implicitly by the shell.
  1623. //
  1624. case '-':
  1625. Result = ShCreateOptionsString(Shell, &Value, &ValueSize);
  1626. AllocatedValue = Value;
  1627. break;
  1628. //
  1629. // The dollar sign expands to the decimal process ID of the invoked
  1630. // shell. Subshells retain the same process ID as their parent shells.
  1631. //
  1632. case '$':
  1633. ValueSize = snprintf(LocalBuffer,
  1634. SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE,
  1635. "%d",
  1636. Shell->ProcessId);
  1637. if (ValueSize == -1) {
  1638. goto ExpandSpecialParameterEnd;
  1639. }
  1640. ValueSize += 1;
  1641. Value = LocalBuffer;
  1642. Result = TRUE;
  1643. break;
  1644. //
  1645. // The ! character expands to the decimal process ID of the most
  1646. // recent background command executed from the current shell.
  1647. // Background commands in subshells don't affect this parameter.
  1648. //
  1649. case '!':
  1650. ValueSize = snprintf(LocalBuffer,
  1651. SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE,
  1652. "%d",
  1653. Shell->LastBackgroundProcessId);
  1654. if (ValueSize == -1) {
  1655. goto ExpandSpecialParameterEnd;
  1656. }
  1657. ValueSize += 1;
  1658. Value = LocalBuffer;
  1659. Result = TRUE;
  1660. break;
  1661. default:
  1662. assert(FALSE);
  1663. return FALSE;
  1664. }
  1665. }
  1666. if (Result == FALSE) {
  1667. goto ExpandSpecialParameterEnd;
  1668. }
  1669. //
  1670. // Make sure the value is heap allocated.
  1671. //
  1672. if ((AllocatedValue == NULL) && (Value != NULL)) {
  1673. AllocatedValue = SwStringDuplicate(Value, ValueSize);
  1674. if (AllocatedValue == NULL) {
  1675. Result = FALSE;
  1676. goto ExpandSpecialParameterEnd;
  1677. }
  1678. Value = AllocatedValue;
  1679. }
  1680. Result = ShEscapeSpecialCharacters(Quoted, &Value, &ValueSize);
  1681. if (Result == FALSE) {
  1682. goto ExpandSpecialParameterEnd;
  1683. }
  1684. AllocatedValue = Value;
  1685. //
  1686. // Replace the expansion region with the final value.
  1687. //
  1688. Result = SwStringReplaceRegion(StringBufferAddress,
  1689. StringBufferSize,
  1690. StringBufferCapacity,
  1691. ExpansionOuterBegin,
  1692. ExpansionOuterEnd,
  1693. Value,
  1694. ValueSize);
  1695. if (Result == FALSE) {
  1696. goto ExpandSpecialParameterEnd;
  1697. }
  1698. if (ValueSize != 0) {
  1699. ValueSize -= 1;
  1700. }
  1701. //
  1702. // Take note of the expansion if requested.
  1703. //
  1704. Result = ShAddExpansionRangeEntry(ExpansionList,
  1705. Type,
  1706. ExpansionOuterBegin,
  1707. ValueSize);
  1708. if (Result == FALSE) {
  1709. goto ExpandSpecialParameterEnd;
  1710. }
  1711. if (ExpansionIndex != NULL) {
  1712. *ExpansionIndex += ValueSize;
  1713. }
  1714. Delta = ValueSize - (ExpansionOuterEnd - ExpansionOuterBegin);
  1715. if (ExpansionEndIndex != NULL) {
  1716. *ExpansionEndIndex += Delta;
  1717. }
  1718. ExpandSpecialParameterEnd:
  1719. if (AllocatedValue != NULL) {
  1720. free(AllocatedValue);
  1721. }
  1722. return Result;
  1723. }
  1724. BOOL
  1725. ShExpandSubshell (
  1726. PSHELL Shell,
  1727. BOOL Quoted,
  1728. PSTR *StringBufferAddress,
  1729. PUINTN StringBufferSize,
  1730. PUINTN StringBufferCapacity,
  1731. PUINTN ExpansionIndex,
  1732. PUINTN ExpansionEndIndex,
  1733. PLIST_ENTRY ExpansionList
  1734. )
  1735. /*++
  1736. Routine Description:
  1737. This routine performs command substitution.
  1738. Arguments:
  1739. Shell - Supplies a pointer to the shell.
  1740. Quoted - Supplies a boolean indicating whether the expansion is in double
  1741. quotes or not.
  1742. StringBufferAddress - Supplies a pointer to the address of the allocated
  1743. string buffer. This value may be changed if the string is reallocated.
  1744. StringBufferSize - Supplies a pointer that on input contains the size of
  1745. the current string in bytes, including the null terminator. On output,
  1746. it will contain the updated size of the string in bytes.
  1747. StringBufferCapacity - Supplies a pointer that on input supplies the total
  1748. size of the buffer. It will contain the updated size of the buffer
  1749. allocation on output.
  1750. ExpansionIndex - Supplies a pointer that on input contains the index of
  1751. the first character of the expansion. On output, will contain the index
  1752. immediately after the expansion.
  1753. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  1754. index to search for expansions. On output, this will be updated to
  1755. reflect any expansions.
  1756. ExpansionList - Supplies an optional pointer to the list of expansions
  1757. that have occurred on this buffer. Any expansions here will be added
  1758. to the end of this list.
  1759. Return Value:
  1760. TRUE on success.
  1761. FALSE on failure.
  1762. --*/
  1763. {
  1764. UINTN Delta;
  1765. BOOL DequoteForSubshell;
  1766. PSTR Input;
  1767. UINTN InputIndex;
  1768. UINTN InputSize;
  1769. UINTN OuterEndIndex;
  1770. PSTR Output;
  1771. UINTN OutputSize;
  1772. UINTN QuoteIndex;
  1773. BOOL Result;
  1774. INT ReturnValue;
  1775. PSTR String;
  1776. PSHELL Subshell;
  1777. BOOL WasBackslash;
  1778. assert(*ExpansionIndex <= *StringBufferSize);
  1779. Input = NULL;
  1780. Output = NULL;
  1781. OutputSize = 0;
  1782. String = *StringBufferAddress;
  1783. Subshell = NULL;
  1784. InputIndex = *ExpansionIndex;
  1785. Result = ShScanPastExpansion(String + InputIndex,
  1786. *ExpansionEndIndex - InputIndex,
  1787. &InputSize);
  1788. if (Result == FALSE) {
  1789. return FALSE;
  1790. }
  1791. assert(InputSize > 0);
  1792. OuterEndIndex = InputIndex + InputSize;
  1793. //
  1794. // Move the inner string in to remove the `...` or $(...).
  1795. //
  1796. DequoteForSubshell = FALSE;
  1797. if (String[InputIndex] == '`') {
  1798. InputIndex += 1;
  1799. InputSize -= 2;
  1800. DequoteForSubshell = TRUE;
  1801. } else {
  1802. assert(String[InputIndex] == '$');
  1803. InputIndex += 2;
  1804. InputSize -= 3;
  1805. }
  1806. //
  1807. // Create a copy of the input.
  1808. //
  1809. InputSize += 1;
  1810. Input = SwStringDuplicate(String + InputIndex, InputSize);
  1811. if (Input == NULL) {
  1812. Result = FALSE;
  1813. goto ExpandSubshellEnd;
  1814. }
  1815. //
  1816. // If already inside of double quotes, remove any backslashes in a \"
  1817. // combination.
  1818. //
  1819. if ((Quoted != FALSE) && (DequoteForSubshell != FALSE)) {
  1820. WasBackslash = FALSE;
  1821. for (QuoteIndex = 0; QuoteIndex < InputSize; QuoteIndex += 1) {
  1822. if ((WasBackslash != FALSE) && (Input[QuoteIndex] == '"')) {
  1823. SwStringRemoveRegion(Input, &InputSize, QuoteIndex - 1, 1);
  1824. QuoteIndex -= 1;
  1825. WasBackslash = FALSE;
  1826. continue;
  1827. }
  1828. if (Input[QuoteIndex] == '\\') {
  1829. WasBackslash = !WasBackslash;
  1830. } else {
  1831. WasBackslash = FALSE;
  1832. }
  1833. }
  1834. }
  1835. //
  1836. // Create and execute a subshell.
  1837. //
  1838. Subshell = ShCreateSubshell(Shell, Input, InputSize, DequoteForSubshell);
  1839. if (Subshell == NULL) {
  1840. Result = FALSE;
  1841. goto ExpandSubshellEnd;
  1842. }
  1843. Result = ShExecuteSubshell(Shell,
  1844. Subshell,
  1845. FALSE,
  1846. &Output,
  1847. &OutputSize,
  1848. &ReturnValue);
  1849. if (Result == FALSE) {
  1850. goto ExpandSubshellEnd;
  1851. }
  1852. //
  1853. // Save the subshell's result as the most recent result in this parent
  1854. // shell.
  1855. //
  1856. Shell->ReturnValue = ReturnValue;
  1857. //
  1858. // Remove any trailing newlines from the output.
  1859. //
  1860. if (OutputSize != 0) {
  1861. while ((OutputSize != 0) &&
  1862. ((Output[OutputSize - 1] == '\n') ||
  1863. (Output[OutputSize - 1] == '\r'))) {
  1864. OutputSize -= 1;
  1865. }
  1866. //
  1867. // Add one for the presumed (but not used) null terminator.
  1868. //
  1869. OutputSize += 1;
  1870. }
  1871. //
  1872. // Escape any fancy characters that shouldn't get interpreted by the shell.
  1873. //
  1874. Result = ShEscapeSpecialCharacters(Quoted, &Output, &OutputSize);
  1875. if (Result == FALSE) {
  1876. goto ExpandSubshellEnd;
  1877. }
  1878. //
  1879. // Now replace the expansion with the output.
  1880. //
  1881. Result = SwStringReplaceRegion(StringBufferAddress,
  1882. StringBufferSize,
  1883. StringBufferCapacity,
  1884. *ExpansionIndex,
  1885. OuterEndIndex,
  1886. Output,
  1887. OutputSize);
  1888. if (Result == FALSE) {
  1889. goto ExpandSubshellEnd;
  1890. }
  1891. if (OutputSize != 0) {
  1892. OutputSize -= 1;
  1893. }
  1894. //
  1895. // Take note of the expansion if requested.
  1896. //
  1897. Result = ShAddExpansionRangeEntry(ExpansionList,
  1898. ShellExpansionFieldSplit,
  1899. *ExpansionIndex,
  1900. OutputSize);
  1901. if (Result == FALSE) {
  1902. goto ExpandSubshellEnd;
  1903. }
  1904. Delta = OutputSize - (OuterEndIndex - *ExpansionIndex);
  1905. *ExpansionEndIndex += Delta;
  1906. *ExpansionIndex += OutputSize;
  1907. ExpandSubshellEnd:
  1908. if (Subshell != NULL) {
  1909. ShDestroyShell(Subshell);
  1910. }
  1911. if (Input != NULL) {
  1912. free(Input);
  1913. }
  1914. if (Output != NULL) {
  1915. free(Output);
  1916. }
  1917. return Result;
  1918. }
  1919. BOOL
  1920. ShExpandTilde (
  1921. PSHELL Shell,
  1922. PSTR *StringBufferAddress,
  1923. PUINTN StringBufferSize,
  1924. PUINTN StringBufferCapacity,
  1925. PUINTN ExpansionIndex,
  1926. PUINTN ExpansionEndIndex,
  1927. PLIST_ENTRY ExpansionList
  1928. )
  1929. /*++
  1930. Routine Description:
  1931. This routine performs tilde expansion.
  1932. Arguments:
  1933. Shell - Supplies a pointer to the shell.
  1934. StringBufferAddress - Supplies a pointer to the address of the allocated
  1935. string buffer. This value may be changed if the string is reallocated.
  1936. StringBufferSize - Supplies a pointer that on input contains the size of
  1937. the current string in bytes, including the null terminator. On output,
  1938. it will contain the updated size of the string in bytes.
  1939. StringBufferCapacity - Supplies a pointer that on input supplies the total
  1940. size of the buffer. It will contain the updated size of the buffer
  1941. allocation on output.
  1942. ExpansionIndex - Supplies a pointer that on input contains the index of
  1943. the first character of the expansion. On output, will contain the index
  1944. immediately after the expansion.
  1945. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  1946. index to search for expansions. On output, this will be updated to
  1947. reflect any expansions.
  1948. ExpansionList - Supplies an optional pointer to the list of expansions
  1949. that have occurred on this buffer. Any expansions here will be added
  1950. to the end of this list.
  1951. Return Value:
  1952. TRUE on success.
  1953. FALSE on failure.
  1954. --*/
  1955. {
  1956. PSTR AllocatedHome;
  1957. UINTN Delta;
  1958. BOOL DidExpansion;
  1959. PSTR Home;
  1960. INT HomeSize;
  1961. UINTN HomeSizeBig;
  1962. UINTN InputIndex;
  1963. UINTN InputSize;
  1964. UINTN OuterEndIndex;
  1965. BOOL Result;
  1966. PSTR String;
  1967. assert(*ExpansionIndex <= *StringBufferSize);
  1968. AllocatedHome = NULL;
  1969. DidExpansion = FALSE;
  1970. Home = NULL;
  1971. HomeSize = 0;
  1972. String = *StringBufferAddress;
  1973. InputIndex = *ExpansionIndex;
  1974. assert(String[InputIndex] == '~');
  1975. Result = ShScanPastExpansion(String + InputIndex,
  1976. *ExpansionEndIndex - InputIndex,
  1977. &InputSize);
  1978. if (Result == FALSE) {
  1979. return FALSE;
  1980. }
  1981. assert(InputSize > 0);
  1982. OuterEndIndex = InputIndex + InputSize;
  1983. //
  1984. // Move the inner string in to remove the ~
  1985. //
  1986. InputIndex += 1;
  1987. InputSize -= 1;
  1988. //
  1989. // If there was no user specified, just get the value of the home variable.
  1990. //
  1991. if (InputSize == 0) {
  1992. Result = ShGetVariable(Shell,
  1993. SHELL_HOME,
  1994. sizeof(SHELL_HOME),
  1995. &Home,
  1996. &HomeSizeBig);
  1997. if (Result == FALSE) {
  1998. Result = TRUE;
  1999. goto ExpandTildeEnd;
  2000. }
  2001. HomeSize = HomeSizeBig;
  2002. //
  2003. // Get the home directory of a specific user.
  2004. //
  2005. } else {
  2006. Result = ShGetHomeDirectory(String + InputIndex,
  2007. InputSize + 1,
  2008. &AllocatedHome,
  2009. &HomeSize);
  2010. if (Result == FALSE) {
  2011. Result = TRUE;
  2012. goto ExpandTildeEnd;
  2013. }
  2014. Home = AllocatedHome;
  2015. }
  2016. //
  2017. // Now replace the expansion with the home path.
  2018. //
  2019. Result = SwStringReplaceRegion(StringBufferAddress,
  2020. StringBufferSize,
  2021. StringBufferCapacity,
  2022. *ExpansionIndex,
  2023. OuterEndIndex,
  2024. Home,
  2025. HomeSize);
  2026. if (Result == FALSE) {
  2027. goto ExpandTildeEnd;
  2028. }
  2029. if (HomeSize != 0) {
  2030. HomeSize -= 1;
  2031. }
  2032. //
  2033. // Take note of the expansion if requested.
  2034. //
  2035. Result = ShAddExpansionRangeEntry(ExpansionList,
  2036. ShellExpansionFieldSplit,
  2037. *ExpansionIndex,
  2038. HomeSize);
  2039. if (Result == FALSE) {
  2040. goto ExpandTildeEnd;
  2041. }
  2042. Delta = HomeSize - (OuterEndIndex - *ExpansionIndex);
  2043. *ExpansionEndIndex += Delta;
  2044. *ExpansionIndex += HomeSize;
  2045. DidExpansion = TRUE;
  2046. ExpandTildeEnd:
  2047. //
  2048. // If the expansion wasn't done, move the current expansion index past the
  2049. // expansion so this routine doesn't get called again for the same spot.
  2050. //
  2051. if ((Result != FALSE) && (DidExpansion == FALSE)) {
  2052. *ExpansionIndex = OuterEndIndex;
  2053. }
  2054. if (AllocatedHome != NULL) {
  2055. free(AllocatedHome);
  2056. }
  2057. return Result;
  2058. }
  2059. BOOL
  2060. ShExpandArithmeticExpression (
  2061. PSHELL Shell,
  2062. PSTR *StringBufferAddress,
  2063. PUINTN StringBufferSize,
  2064. PUINTN StringBufferCapacity,
  2065. PUINTN ExpansionIndex,
  2066. PUINTN ExpansionEndIndex,
  2067. PLIST_ENTRY ExpansionList
  2068. )
  2069. /*++
  2070. Routine Description:
  2071. This routine performs arithmetic expression expansion.
  2072. Arguments:
  2073. Shell - Supplies a pointer to the shell.
  2074. StringBufferAddress - Supplies a pointer to the address of the allocated
  2075. string buffer. This value may be changed if the string is reallocated.
  2076. StringBufferSize - Supplies a pointer that on input contains the size of
  2077. the current string in bytes, including the null terminator. On output,
  2078. it will contain the updated size of the string in bytes.
  2079. StringBufferCapacity - Supplies a pointer that on input supplies the total
  2080. size of the buffer. It will contain the updated size of the buffer
  2081. allocation on output.
  2082. ExpansionIndex - Supplies a pointer that on input contains the index of
  2083. the first character of the expansion. On output, will contain the index
  2084. immediately after the expansion.
  2085. ExpansionEndIndex - Supplies a pointer that on input contains the ending
  2086. index to search for expansions. On output, this will be updated to
  2087. reflect any expansions.
  2088. ExpansionList - Supplies an optional pointer to the list of expansions
  2089. that have occurred on this buffer. Any expansions here will be added
  2090. to the end of this list.
  2091. Return Value:
  2092. TRUE on success.
  2093. FALSE on failure.
  2094. --*/
  2095. {
  2096. UINTN Delta;
  2097. PSTR ExpandedString;
  2098. UINTN ExpandedStringSize;
  2099. UINTN InputIndex;
  2100. UINTN InputSize;
  2101. ULONG Options;
  2102. UINTN OuterIndex;
  2103. PSTR Output;
  2104. UINTN OutputSize;
  2105. BOOL Result;
  2106. PSTR String;
  2107. assert(*ExpansionIndex <= *StringBufferSize);
  2108. ExpandedString = NULL;
  2109. Output = NULL;
  2110. OutputSize = 0;
  2111. String = *StringBufferAddress;
  2112. InputIndex = *ExpansionIndex;
  2113. Result = ShScanPastExpansion(String + InputIndex,
  2114. *ExpansionEndIndex - InputIndex,
  2115. &InputSize);
  2116. if (Result == FALSE) {
  2117. return FALSE;
  2118. }
  2119. assert(InputSize > 5);
  2120. //
  2121. // Move the input beyond the $((, and decrease the size to remove both $((
  2122. // and )).
  2123. //
  2124. OuterIndex = InputIndex + InputSize;
  2125. InputIndex += 3;
  2126. InputSize -= 5;
  2127. //
  2128. // Expand anything inside the expression.
  2129. //
  2130. Options = SHELL_EXPANSION_OPTION_NO_TILDE_EXPANSION |
  2131. SHELL_EXPANSION_OPTION_NO_FIELD_SPLIT;
  2132. Result = ShPerformExpansions(Shell,
  2133. String + InputIndex,
  2134. InputSize + 1,
  2135. Options,
  2136. &ExpandedString,
  2137. &ExpandedStringSize,
  2138. NULL,
  2139. NULL);
  2140. if (Result == FALSE) {
  2141. goto ExpandArithmeticExpressionEnd;
  2142. }
  2143. //
  2144. // Evaluate the arithmetic statement.
  2145. //
  2146. Result = ShEvaluateArithmeticExpression(Shell,
  2147. ExpandedString,
  2148. ExpandedStringSize,
  2149. &Output,
  2150. &OutputSize);
  2151. if (Result == FALSE) {
  2152. goto ExpandArithmeticExpressionEnd;
  2153. }
  2154. //
  2155. // Now replace the expansion with the output.
  2156. //
  2157. Result = SwStringReplaceRegion(StringBufferAddress,
  2158. StringBufferSize,
  2159. StringBufferCapacity,
  2160. *ExpansionIndex,
  2161. OuterIndex,
  2162. Output,
  2163. OutputSize);
  2164. if (Result == FALSE) {
  2165. goto ExpandArithmeticExpressionEnd;
  2166. }
  2167. if (OutputSize != 0) {
  2168. OutputSize -= 1;
  2169. }
  2170. //
  2171. // Take note of the expansion if requested.
  2172. //
  2173. Result = ShAddExpansionRangeEntry(ExpansionList,
  2174. ShellExpansionFieldSplit,
  2175. *ExpansionIndex,
  2176. OutputSize);
  2177. if (Result == FALSE) {
  2178. goto ExpandArithmeticExpressionEnd;
  2179. }
  2180. Delta = OutputSize - (OuterIndex - *ExpansionIndex);
  2181. *ExpansionEndIndex += Delta;
  2182. *ExpansionIndex += OutputSize;
  2183. ExpandArithmeticExpressionEnd:
  2184. if (Output != NULL) {
  2185. free(Output);
  2186. }
  2187. if (ExpandedString != NULL) {
  2188. free(ExpandedString);
  2189. }
  2190. return Result;
  2191. }
  2192. BOOL
  2193. ShCreateAllParametersString (
  2194. PSHELL Shell,
  2195. CHAR Separator,
  2196. PSTR *NewString,
  2197. PUINTN NewStringSize
  2198. )
  2199. /*++
  2200. Routine Description:
  2201. This routine creates a string containing all the positional arguments, not
  2202. including the command name.
  2203. Arguments:
  2204. Shell - Supplies a pointer to the shell.
  2205. Separator - Supplies the separator character to use between each
  2206. positional arguments.
  2207. NewString - Supplies a pointer where a pointer to the string containing
  2208. all the arguments will be returned on success. The caller is
  2209. responsible for freeing this memory.
  2210. NewStringSize - Supplies a pointer where the size of the new string buffer
  2211. including the null terminator will be returned on success.
  2212. Return Value:
  2213. TRUE on success.
  2214. FALSE on failure.
  2215. --*/
  2216. {
  2217. PSHELL_ARGUMENT Argument;
  2218. PLIST_ENTRY ArgumentList;
  2219. UINTN BufferSize;
  2220. PLIST_ENTRY CurrentEntry;
  2221. PSTR CurrentString;
  2222. PSTR Line;
  2223. BOOL NoSeparator;
  2224. BOOL Result;
  2225. PSTR Separators;
  2226. UINTN SeparatorsSize;
  2227. ArgumentList = ShGetCurrentArgumentList(Shell);
  2228. Line = NULL;
  2229. BufferSize = 0;
  2230. if (LIST_EMPTY(ArgumentList) != FALSE) {
  2231. Result = TRUE;
  2232. goto CreateAllParametersStringEnd;
  2233. }
  2234. NoSeparator = FALSE;
  2235. BufferSize = 0;
  2236. //
  2237. // If the separator is space, then it's a $* expansion. Use the first
  2238. // character of the IFS variable, none if IFS is set to NULL, or a space if
  2239. // IFS is unset.
  2240. //
  2241. if (Separator == ' ') {
  2242. Result = ShGetVariable(Shell,
  2243. SHELL_IFS,
  2244. sizeof(SHELL_IFS),
  2245. &Separators,
  2246. &SeparatorsSize);
  2247. if (Result != FALSE) {
  2248. assert(SeparatorsSize != 0);
  2249. Separator = Separators[0];
  2250. if (Separator == '\0') {
  2251. NoSeparator = TRUE;
  2252. }
  2253. }
  2254. }
  2255. Result = FALSE;
  2256. //
  2257. // Loop through once to figure out how big this buffer needs to be.
  2258. //
  2259. CurrentEntry = ArgumentList->Next;
  2260. while (CurrentEntry != ArgumentList) {
  2261. Argument = LIST_VALUE(CurrentEntry, SHELL_ARGUMENT, ListEntry);
  2262. CurrentEntry = CurrentEntry->Next;
  2263. assert(Argument->NameSize != 0);
  2264. //
  2265. // The buffer space needed is the size of the string minus one because
  2266. // of the null terminator plus one because of space.
  2267. //
  2268. BufferSize += Argument->NameSize - 1;
  2269. if (NoSeparator == FALSE) {
  2270. BufferSize += 1;
  2271. }
  2272. }
  2273. //
  2274. // Add one for the null terminator if the superfluous separator wasn't
  2275. // added.
  2276. //
  2277. if (NoSeparator != FALSE) {
  2278. BufferSize += 1;
  2279. }
  2280. //
  2281. // Allocate the buffer.
  2282. //
  2283. Line = malloc(BufferSize);
  2284. if (Line == NULL) {
  2285. goto CreateAllParametersStringEnd;
  2286. }
  2287. CurrentString = Line;
  2288. //
  2289. // Loop through again and copy the parameters in, separated by spaces.
  2290. //
  2291. CurrentEntry = ArgumentList->Next;
  2292. while (CurrentEntry != ArgumentList) {
  2293. Argument = LIST_VALUE(CurrentEntry, SHELL_ARGUMENT, ListEntry);
  2294. CurrentEntry = CurrentEntry->Next;
  2295. assert(Argument->NameSize != 0);
  2296. strcpy(CurrentString, Argument->Name);
  2297. CurrentString += Argument->NameSize - 1;
  2298. if (NoSeparator == FALSE) {
  2299. *CurrentString = Separator;
  2300. CurrentString += 1;
  2301. }
  2302. }
  2303. //
  2304. // That last space isn't needed so back it out and make it the null
  2305. // terminator.
  2306. //
  2307. if (NoSeparator == FALSE) {
  2308. CurrentString -= 1;
  2309. }
  2310. *CurrentString = '\0';
  2311. CurrentString += 1;
  2312. assert((UINTN)CurrentString - (UINTN)Line == BufferSize);
  2313. Result = TRUE;
  2314. CreateAllParametersStringEnd:
  2315. if (Result == FALSE) {
  2316. BufferSize = 0;
  2317. if (Line != NULL) {
  2318. free(Line);
  2319. Line = NULL;
  2320. }
  2321. }
  2322. *NewString = Line;
  2323. *NewStringSize = BufferSize;
  2324. return Result;
  2325. }
  2326. BOOL
  2327. ShCreateParameterCountString (
  2328. PSHELL Shell,
  2329. PSTR *NewString,
  2330. PUINTN NewStringSize
  2331. )
  2332. /*++
  2333. Routine Description:
  2334. This routine creates a string containing the number of command arguments to
  2335. the most recent function or shell invocation.
  2336. Arguments:
  2337. Shell - Supplies a pointer to the shell.
  2338. NewString - Supplies a pointer where a pointer to the string containing
  2339. all the arguments will be returned on success. The caller is
  2340. responsible for freeing this memory.
  2341. NewStringSize - Supplies a pointer where the size of the new string buffer
  2342. including the null terminator will be returned on success.
  2343. Return Value:
  2344. TRUE on success.
  2345. FALSE on failure.
  2346. --*/
  2347. {
  2348. ULONG ArgumentCount;
  2349. PLIST_ENTRY ArgumentList;
  2350. UINTN BufferSize;
  2351. PLIST_ENTRY CurrentEntry;
  2352. PSTR Line;
  2353. CHAR LocalBuffer[SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE];
  2354. BOOL Result;
  2355. ArgumentCount = 0;
  2356. ArgumentList = ShGetCurrentArgumentList(Shell);
  2357. Line = NULL;
  2358. BufferSize = 0;
  2359. Result = FALSE;
  2360. //
  2361. // Loop through to count arguments.
  2362. //
  2363. CurrentEntry = ArgumentList->Next;
  2364. while (CurrentEntry != ArgumentList) {
  2365. CurrentEntry = CurrentEntry->Next;
  2366. ArgumentCount += 1;
  2367. }
  2368. //
  2369. // Convert that number using a local buffer.
  2370. //
  2371. BufferSize = snprintf(LocalBuffer,
  2372. SHELL_ARGUMENT_COUNT_STRING_BUFFER_SIZE,
  2373. "%d",
  2374. ArgumentCount);
  2375. if (BufferSize == -1) {
  2376. goto CreateParameterCountStringEnd;
  2377. }
  2378. //
  2379. // Include one for the null terminator and copy the string.
  2380. //
  2381. BufferSize += 1;
  2382. Line = SwStringDuplicate(LocalBuffer, BufferSize);
  2383. if (Line == NULL) {
  2384. goto CreateParameterCountStringEnd;
  2385. }
  2386. Result = TRUE;
  2387. CreateParameterCountStringEnd:
  2388. if (Result == FALSE) {
  2389. BufferSize = 0;
  2390. if (Line != NULL) {
  2391. free(Line);
  2392. Line = NULL;
  2393. }
  2394. }
  2395. *NewString = Line;
  2396. *NewStringSize = BufferSize;
  2397. return Result;
  2398. }
  2399. BOOL
  2400. ShCreateOptionsString (
  2401. PSHELL Shell,
  2402. PSTR *NewString,
  2403. PUINTN NewStringSize
  2404. )
  2405. /*++
  2406. Routine Description:
  2407. This routine creates a string containing the single letter options used or
  2408. the current shell invocation.
  2409. Arguments:
  2410. Shell - Supplies a pointer to the shell.
  2411. NewString - Supplies a pointer where a pointer to the string containing
  2412. all the options will be returned on success. The caller is
  2413. responsible for freeing this memory.
  2414. NewStringSize - Supplies a pointer where the size of the new string buffer
  2415. including the null terminator will be returned on success.
  2416. Return Value:
  2417. TRUE on success.
  2418. FALSE on failure.
  2419. --*/
  2420. {
  2421. ULONG Options;
  2422. PSTR OptionsString;
  2423. UINTN OutputIndex;
  2424. OptionsString = malloc(SHELL_OPTION_STRING_SIZE);
  2425. if (OptionsString == NULL) {
  2426. *NewString = NULL;
  2427. *NewStringSize = 0;
  2428. return FALSE;
  2429. }
  2430. memset(OptionsString, 0, SHELL_OPTION_STRING_SIZE);
  2431. Options = Shell->Options;
  2432. OutputIndex = 0;
  2433. //
  2434. // Look at each option.
  2435. //
  2436. if ((Options & SHELL_OPTION_EXPORT_ALL) != 0) {
  2437. OptionsString[OutputIndex] = 'a';
  2438. OutputIndex += 1;
  2439. }
  2440. if ((Options & SHELL_OPTION_ASYNCHRONOUS_JOB_NOTIFICATION) != 0) {
  2441. OptionsString[OutputIndex] = 'b';
  2442. OutputIndex += 1;
  2443. }
  2444. if ((Options & SHELL_OPTION_NO_CLOBBER) != 0) {
  2445. OptionsString[OutputIndex] = 'C';
  2446. OutputIndex += 1;
  2447. }
  2448. if ((Options & SHELL_OPTION_DEBUG) != 0) {
  2449. OptionsString[OutputIndex] = 'd';
  2450. OutputIndex += 1;
  2451. }
  2452. if ((Options & SHELL_OPTION_EXIT_ON_FAILURE) != 0) {
  2453. OptionsString[OutputIndex] = 'e';
  2454. OutputIndex += 1;
  2455. }
  2456. if ((Options & SHELL_OPTION_NO_PATHNAME_EXPANSION) != 0) {
  2457. OptionsString[OutputIndex] = 'f';
  2458. OutputIndex += 1;
  2459. }
  2460. if ((Options & SHELL_OPTION_LOCATE_UTILITIES_IN_DECLARATION) != 0) {
  2461. OptionsString[OutputIndex] = 'h';
  2462. OutputIndex += 1;
  2463. }
  2464. if ((Options & SHELL_OPTION_INTERACTIVE) != 0) {
  2465. OptionsString[OutputIndex] = 'i';
  2466. OutputIndex += 1;
  2467. }
  2468. if ((Options & SHELL_OPTION_RUN_JOBS_IN_SEPARATE_PROCESS_GROUP) != 0) {
  2469. OptionsString[OutputIndex] = 'm';
  2470. OutputIndex += 1;
  2471. }
  2472. if ((Options & SHELL_OPTION_NO_EXECUTE) != 0) {
  2473. OptionsString[OutputIndex] = 'n';
  2474. OutputIndex += 1;
  2475. }
  2476. if ((Options & SHELL_OPTION_READ_FROM_STDIN) != 0) {
  2477. OptionsString[OutputIndex] = 's';
  2478. OutputIndex += 1;
  2479. }
  2480. if ((Options & SHELL_OPTION_EXIT_ON_UNSET_VARIABLE) != 0) {
  2481. OptionsString[OutputIndex] = 'u';
  2482. OutputIndex += 1;
  2483. }
  2484. if ((Options & SHELL_OPTION_DISPLAY_INPUT) != 0) {
  2485. OptionsString[OutputIndex] = 'v';
  2486. OutputIndex += 1;
  2487. }
  2488. if ((Options & SHELL_OPTION_TRACE_COMMAND) != 0) {
  2489. OptionsString[OutputIndex] = 'x';
  2490. OutputIndex += 1;
  2491. }
  2492. assert(OutputIndex < SHELL_OPTION_STRING_SIZE);
  2493. *NewString = OptionsString;
  2494. *NewStringSize = OutputIndex + 1;
  2495. return TRUE;
  2496. }
  2497. VOID
  2498. ShGetPositionalArgument (
  2499. PSHELL Shell,
  2500. ULONG ArgumentNumber,
  2501. PSTR *Argument,
  2502. PUINTN ArgumentSize
  2503. )
  2504. /*++
  2505. Routine Description:
  2506. This routine returns the value for a positional argument.
  2507. Arguments:
  2508. Shell - Supplies a pointer to the shell.
  2509. ArgumentNumber - Supplies the argument number to get. Zero gets the command,
  2510. one gets the first positional argument.
  2511. Argument - Supplies a pointer where a pointer to the argument string will
  2512. be returned on success. The caller does not own this buffer, and must
  2513. not free it.
  2514. ArgumentSize - Supplies a pointer where the size of the returned argument
  2515. buffer in bytes including the null terminator will be returned.
  2516. Return Value:
  2517. None.
  2518. --*/
  2519. {
  2520. PSHELL_ARGUMENT ArgumentEntry;
  2521. ULONG ArgumentIndex;
  2522. PLIST_ENTRY ArgumentList;
  2523. PLIST_ENTRY CurrentEntry;
  2524. if (ArgumentNumber == 0) {
  2525. *Argument = Shell->CommandName;
  2526. *ArgumentSize = Shell->CommandNameSize;
  2527. return;
  2528. }
  2529. ArgumentList = ShGetCurrentArgumentList(Shell);
  2530. ArgumentIndex = 1;
  2531. CurrentEntry = ArgumentList->Next;
  2532. while (CurrentEntry != ArgumentList) {
  2533. ArgumentEntry = LIST_VALUE(CurrentEntry, SHELL_ARGUMENT, ListEntry);
  2534. if (ArgumentIndex == ArgumentNumber) {
  2535. *Argument = ArgumentEntry->Name;
  2536. *ArgumentSize = ArgumentEntry->NameSize;
  2537. return;
  2538. }
  2539. ArgumentIndex += 1;
  2540. CurrentEntry = CurrentEntry->Next;
  2541. }
  2542. *Argument = NULL;
  2543. *ArgumentSize = 0;
  2544. return;
  2545. }
  2546. BOOL
  2547. ShAddExpansionRangeEntry (
  2548. PLIST_ENTRY ListHead,
  2549. SHELL_EXPANSION_TYPE Type,
  2550. UINTN Index,
  2551. UINTN Length
  2552. )
  2553. /*++
  2554. Routine Description:
  2555. This routine allocates an expansion range entry, initializes it, and
  2556. places it on the end of the given list.
  2557. Arguments:
  2558. ListHead - Supplies an optional pointer to the head of the list. If NULL,
  2559. this routine is a no-op.
  2560. Type - Supplies the expansion type.
  2561. Index - Supplies the byte offset index of the expansion.
  2562. Length - Supplies the length of the expansion in bytes.
  2563. Return Value:
  2564. TRUE on success.
  2565. FALSE on failure.
  2566. --*/
  2567. {
  2568. PSHELL_EXPANSION_RANGE Range;
  2569. if ((ListHead == NULL) ||
  2570. ((Length == 0) && (Type != ShellExpansionSplitOnNull))) {
  2571. return TRUE;
  2572. }
  2573. Range = malloc(sizeof(SHELL_EXPANSION_RANGE));
  2574. if (Range == NULL) {
  2575. return FALSE;
  2576. }
  2577. Range->Type = Type;
  2578. Range->Index = Index;
  2579. Range->Length = Length;
  2580. INSERT_BEFORE(&(Range->ListEntry), ListHead);
  2581. return TRUE;
  2582. }
  2583. VOID
  2584. ShTrimVariableValue (
  2585. PSTR *Value,
  2586. PUINTN ValueSize,
  2587. PSTR Pattern,
  2588. UINTN PatternSize,
  2589. BOOL Prefix,
  2590. BOOL Longest
  2591. )
  2592. /*++
  2593. Routine Description:
  2594. This routine trims off the shortest or longest prefix or suffix pattern
  2595. from the given value.
  2596. Arguments:
  2597. Value - Supplies a pointer that on input contains the value to trim. On
  2598. output, this pointer may be moved up to trim a prefix.
  2599. ValueSize - Supplies a pointer that on input contains the size of the
  2600. value string in bytes, including the null terminator. On output, this
  2601. value may be decreased to represent the trim.
  2602. Pattern - Supplies a pointer to the pattern to remove.
  2603. PatternSize - Supplies the size of the pattern buffer in bytes including
  2604. the null terminating character.
  2605. Prefix - Supplies a boolean indicating whether to trim the prefix (TRUE) or
  2606. suffix (FALSE).
  2607. Longest - Supplies a boolean indicating whether to trim the longest
  2608. matching term (TRUE) or the shortest matching term (FALSE).
  2609. Return Value:
  2610. None.
  2611. --*/
  2612. {
  2613. BOOL Match;
  2614. UINTN Size;
  2615. UINTN ValueIndex;
  2616. if (*ValueSize <= 1) {
  2617. return;
  2618. }
  2619. if (PatternSize == 0) {
  2620. return;
  2621. }
  2622. //
  2623. // Determine where to start matching for patterns.
  2624. //
  2625. if (Prefix != FALSE) {
  2626. if (Longest != FALSE) {
  2627. ValueIndex = 0;
  2628. Size = *ValueSize;
  2629. } else {
  2630. ValueIndex = 0;
  2631. Size = 1;
  2632. }
  2633. } else {
  2634. if (Longest != FALSE) {
  2635. ValueIndex = 0;
  2636. Size = *ValueSize;
  2637. } else {
  2638. ValueIndex = *ValueSize - 1;
  2639. Size = 1;
  2640. }
  2641. }
  2642. //
  2643. // Loop looking for a match.
  2644. //
  2645. while (TRUE) {
  2646. //
  2647. // If the given pattern matches, then return that trimmed value.
  2648. //
  2649. Match = SwDoesPatternMatch(*Value + ValueIndex,
  2650. Size,
  2651. Pattern,
  2652. PatternSize);
  2653. if (Match != FALSE) {
  2654. if (Prefix != FALSE) {
  2655. *Value += Size - 1;
  2656. *ValueSize -= Size - 1;
  2657. } else {
  2658. *ValueSize = ValueIndex + 1;
  2659. }
  2660. return;
  2661. }
  2662. //
  2663. // Move the size and/or index to get the next slightly less aggressive
  2664. // combination.
  2665. //
  2666. if (Prefix != FALSE) {
  2667. if (Longest != FALSE) {
  2668. Size -= 1;
  2669. if (Size == 0) {
  2670. break;
  2671. }
  2672. } else {
  2673. if (Size == *ValueSize) {
  2674. break;
  2675. }
  2676. Size += 1;
  2677. }
  2678. } else {
  2679. if (Longest != FALSE) {
  2680. Size -= 1;
  2681. ValueIndex += 1;
  2682. if (ValueIndex == *ValueSize - 1) {
  2683. break;
  2684. }
  2685. } else {
  2686. if (ValueIndex == 0) {
  2687. break;
  2688. }
  2689. ValueIndex -= 1;
  2690. Size += 1;
  2691. }
  2692. }
  2693. }
  2694. return;
  2695. }
  2696. BOOL
  2697. ShEscapeSpecialCharacters (
  2698. BOOL Quoted,
  2699. PSTR *Value,
  2700. PUINTN ValueSize
  2701. )
  2702. /*++
  2703. Routine Description:
  2704. This routine adds escape control characters in front of every control
  2705. character, as well as any character that might be interpreted by the shell
  2706. if not surrounded by double quotes.
  2707. Arguments:
  2708. Quoted - Supplies a boolean indicating this region is inside double quotes.
  2709. Value - Supplies a pointer that on input contains the value to trim. This
  2710. value must be heap allocated. On output, this pointer may be
  2711. reallocated.
  2712. ValueSize - Supplies a pointer that on input contains the size of the
  2713. value string in bytes, including the null terminator. On output, this
  2714. value may be adjusted to accomodate the escape characters.
  2715. Return Value:
  2716. TRUE on success.
  2717. FALSE on allocation failure.
  2718. --*/
  2719. {
  2720. CHAR Character;
  2721. PSTR NeedsEscaping;
  2722. PSTR NewString;
  2723. UINTN NewStringSize;
  2724. PSTR Source;
  2725. UINTN SourceIndex;
  2726. UINTN SourceSize;
  2727. Source = *Value;
  2728. SourceSize = *ValueSize;
  2729. if (SourceSize == 0) {
  2730. return TRUE;
  2731. }
  2732. SourceSize -= 1;
  2733. NewString = NULL;
  2734. NewStringSize = 0;
  2735. for (SourceIndex = 0; SourceIndex < SourceSize; SourceIndex += 1) {
  2736. Character = Source[SourceIndex];
  2737. if (Quoted != FALSE) {
  2738. if (Character == '\0') {
  2739. NeedsEscaping = NULL;
  2740. } else if (Character == '\\') {
  2741. NeedsEscaping = (PVOID)-1;
  2742. } else {
  2743. NeedsEscaping = strchr(ShQuoteEscapeCharacters, Character);
  2744. }
  2745. //
  2746. // In a non-quoted environment, only the control characters themselves
  2747. // need escaping.
  2748. //
  2749. } else {
  2750. NeedsEscaping = NULL;
  2751. if ((Character == SHELL_CONTROL_QUOTE) ||
  2752. (Character == SHELL_CONTROL_ESCAPE)) {
  2753. NeedsEscaping = (PVOID)-1;
  2754. }
  2755. }
  2756. //
  2757. // If the new string hasn't been allocated and this character also
  2758. // doesn't need quoting, just keep going.
  2759. //
  2760. if (NewStringSize == 0) {
  2761. if (NeedsEscaping == NULL) {
  2762. continue;
  2763. }
  2764. //
  2765. // Oh, here's a character that needs escaping. Allocate the new
  2766. // string and copy all the standard characters so far.
  2767. //
  2768. NewString = malloc((SourceSize * 2) + 1);
  2769. if (NewString == NULL) {
  2770. return FALSE;
  2771. }
  2772. memcpy(NewString, Source, SourceIndex);
  2773. NewStringSize = SourceIndex;
  2774. }
  2775. if (NeedsEscaping != NULL) {
  2776. NewString[NewStringSize] = SHELL_CONTROL_ESCAPE;
  2777. NewStringSize += 1;
  2778. }
  2779. NewString[NewStringSize] = Character;
  2780. NewStringSize += 1;
  2781. }
  2782. //
  2783. // If there were never any fancy characters, then no memory was allocated,
  2784. // and the original string can be returned.
  2785. //
  2786. if (NewStringSize == 0) {
  2787. return TRUE;
  2788. }
  2789. NewString[NewStringSize] = '\0';
  2790. NewStringSize += 1;
  2791. //
  2792. // Return the new string.
  2793. //
  2794. free(Source);
  2795. *Value = NewString;
  2796. *ValueSize = NewStringSize;
  2797. return TRUE;
  2798. }