]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/Shell.c
ShellPkg: Fix LastError environment variable truncation
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file
2 This is THE shell (application)
3
4 Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2013, Hewlett-Packard Development Company, L.P.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Shell.h"
17
18 //
19 // Initialize the global structure
20 //
21 SHELL_INFO ShellInfoObject = {
22 NULL,
23 NULL,
24 FALSE,
25 FALSE,
26 {
27 {{
28 0,
29 0,
30 0,
31 0,
32 0,
33 0,
34 0,
35 0,
36 0
37 }},
38 0,
39 NULL,
40 NULL
41 },
42 {{NULL, NULL}, NULL},
43 {
44 {{NULL, NULL}, NULL},
45 0,
46 0,
47 TRUE
48 },
49 NULL,
50 0,
51 NULL,
52 NULL,
53 NULL,
54 NULL,
55 NULL,
56 {{NULL, NULL}, NULL, NULL},
57 {{NULL, NULL}, NULL, NULL},
58 NULL,
59 NULL,
60 NULL,
61 NULL,
62 NULL,
63 NULL,
64 NULL,
65 NULL,
66 FALSE
67 };
68
69 STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
70 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
71 STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
72
73 /**
74 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
75 feature's enabled state was not known when the shell initially launched.
76
77 @retval EFI_SUCCESS The feature is enabled.
78 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
79 **/
80 EFI_STATUS
81 EFIAPI
82 InternalEfiShellStartCtrlSMonitor(
83 VOID
84 )
85 {
86 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
87 EFI_KEY_DATA KeyData;
88 EFI_STATUS Status;
89
90 Status = gBS->OpenProtocol(
91 gST->ConsoleInHandle,
92 &gEfiSimpleTextInputExProtocolGuid,
93 (VOID**)&SimpleEx,
94 gImageHandle,
95 NULL,
96 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
97 if (EFI_ERROR(Status)) {
98 ShellPrintHiiEx(
99 -1,
100 -1,
101 NULL,
102 STRING_TOKEN (STR_SHELL_NO_IN_EX),
103 ShellInfoObject.HiiHandle);
104 return (EFI_SUCCESS);
105 }
106
107 KeyData.KeyState.KeyToggleState = 0;
108 KeyData.Key.ScanCode = 0;
109 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
110 KeyData.Key.UnicodeChar = L's';
111
112 Status = SimpleEx->RegisterKeyNotify(
113 SimpleEx,
114 &KeyData,
115 NotificationFunction,
116 &ShellInfoObject.CtrlSNotifyHandle1);
117
118 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
119 if (!EFI_ERROR(Status)) {
120 Status = SimpleEx->RegisterKeyNotify(
121 SimpleEx,
122 &KeyData,
123 NotificationFunction,
124 &ShellInfoObject.CtrlSNotifyHandle2);
125 }
126 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
127 KeyData.Key.UnicodeChar = 19;
128
129 if (!EFI_ERROR(Status)) {
130 Status = SimpleEx->RegisterKeyNotify(
131 SimpleEx,
132 &KeyData,
133 NotificationFunction,
134 &ShellInfoObject.CtrlSNotifyHandle2);
135 }
136 KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
137 if (!EFI_ERROR(Status)) {
138 Status = SimpleEx->RegisterKeyNotify(
139 SimpleEx,
140 &KeyData,
141 NotificationFunction,
142 &ShellInfoObject.CtrlSNotifyHandle2);
143 }
144 return (Status);
145 }
146
147
148
149 /**
150 The entry point for the application.
151
152 @param[in] ImageHandle The firmware allocated handle for the EFI image.
153 @param[in] SystemTable A pointer to the EFI System Table.
154
155 @retval EFI_SUCCESS The entry point is executed successfully.
156 @retval other Some error occurs when executing this entry point.
157
158 **/
159 EFI_STATUS
160 EFIAPI
161 UefiMain (
162 IN EFI_HANDLE ImageHandle,
163 IN EFI_SYSTEM_TABLE *SystemTable
164 )
165 {
166 EFI_STATUS Status;
167 CHAR16 *TempString;
168 UINTN Size;
169 EFI_HANDLE ConInHandle;
170 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
171
172 if (PcdGet8(PcdShellSupportLevel) > 3) {
173 return (EFI_UNSUPPORTED);
174 }
175
176 //
177 // Clear the screen
178 //
179 Status = gST->ConOut->ClearScreen(gST->ConOut);
180 if (EFI_ERROR(Status)) {
181 return (Status);
182 }
183
184 //
185 // Populate the global structure from PCDs
186 //
187 ShellInfoObject.ImageDevPath = NULL;
188 ShellInfoObject.FileDevPath = NULL;
189 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
190 ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
191 ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
192
193 //
194 // verify we dont allow for spec violation
195 //
196 ASSERT(ShellInfoObject.LogScreenCount >= 3);
197
198 //
199 // Initialize the LIST ENTRY objects...
200 //
201 InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
202 InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
203 InitializeListHead(&ShellInfoObject.SplitList.Link);
204
205 //
206 // Check PCDs for optional features that are not implemented yet.
207 //
208 if ( PcdGetBool(PcdShellSupportOldProtocols)
209 || !FeaturePcdGet(PcdShellRequireHiiPlatform)
210 || FeaturePcdGet(PcdShellSupportFrameworkHii)
211 ) {
212 return (EFI_UNSUPPORTED);
213 }
214
215 //
216 // turn off the watchdog timer
217 //
218 gBS->SetWatchdogTimer (0, 0, 0, NULL);
219
220 //
221 // install our console logger. This will keep a log of the output for back-browsing
222 //
223 Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
224 if (!EFI_ERROR(Status)) {
225 //
226 // Enable the cursor to be visible
227 //
228 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
229
230 //
231 // If supporting EFI 1.1 we need to install HII protocol
232 // only do this if PcdShellRequireHiiPlatform == FALSE
233 //
234 // remove EFI_UNSUPPORTED check above when complete.
235 ///@todo add support for Framework HII
236
237 //
238 // install our (solitary) HII package
239 //
240 ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
241 if (ShellInfoObject.HiiHandle == NULL) {
242 if (PcdGetBool(PcdShellSupportFrameworkHii)) {
243 ///@todo Add our package into Framework HII
244 }
245 if (ShellInfoObject.HiiHandle == NULL) {
246 return (EFI_NOT_STARTED);
247 }
248 }
249
250 //
251 // create and install the EfiShellParametersProtocol
252 //
253 Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
254 ASSERT_EFI_ERROR(Status);
255 ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
256
257 //
258 // create and install the EfiShellProtocol
259 //
260 Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
261 ASSERT_EFI_ERROR(Status);
262 ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
263
264 //
265 // Now initialize the shell library (it requires Shell Parameters protocol)
266 //
267 Status = ShellInitialize();
268 ASSERT_EFI_ERROR(Status);
269
270 Status = CommandInit();
271 ASSERT_EFI_ERROR(Status);
272
273 //
274 // Check the command line
275 //
276 Status = ProcessCommandLine();
277
278 //
279 // If shell support level is >= 1 create the mappings and paths
280 //
281 if (PcdGet8(PcdShellSupportLevel) >= 1) {
282 Status = ShellCommandCreateInitialMappingsAndPaths();
283 }
284
285 //
286 // save the device path for the loaded image and the device path for the filepath (under loaded image)
287 // These are where to look for the startup.nsh file
288 //
289 Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
290 ASSERT_EFI_ERROR(Status);
291
292 //
293 // Display the version
294 //
295 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
296 ShellPrintHiiEx (
297 0,
298 gST->ConOut->Mode->CursorRow,
299 NULL,
300 STRING_TOKEN (STR_VER_OUTPUT_MAIN),
301 ShellInfoObject.HiiHandle,
302 SupportLevel[PcdGet8(PcdShellSupportLevel)],
303 gEfiShellProtocol->MajorVersion,
304 gEfiShellProtocol->MinorVersion,
305 (gST->Hdr.Revision&0xffff0000)>>16,
306 (gST->Hdr.Revision&0x0000ffff),
307 gST->FirmwareVendor,
308 gST->FirmwareRevision
309 );
310 }
311
312 //
313 // Display the mapping
314 //
315 if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
316 Status = RunCommand(L"map");
317 ASSERT_EFI_ERROR(Status);
318 }
319
320 //
321 // init all the built in alias'
322 //
323 Status = SetBuiltInAlias();
324 ASSERT_EFI_ERROR(Status);
325
326 //
327 // Initialize environment variables
328 //
329 if (ShellCommandGetProfileList() != NULL) {
330 Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
331 ASSERT_EFI_ERROR(Status);
332 }
333
334 Size = 100;
335 TempString = AllocateZeroPool(Size);
336
337 UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
338 Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
339 ASSERT_EFI_ERROR(Status);
340
341 UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
342 Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
343 ASSERT_EFI_ERROR(Status);
344
345 UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
346 Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
347 ASSERT_EFI_ERROR(Status);
348
349 FreePool(TempString);
350
351 if (!EFI_ERROR(Status)) {
352 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
353 //
354 // Set up the event for CTRL-C monitoring...
355 //
356 Status = InernalEfiShellStartMonitor();
357 }
358
359 if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
360 //
361 // Set up the event for CTRL-S monitoring...
362 //
363 Status = InternalEfiShellStartCtrlSMonitor();
364 }
365
366 if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
367 //
368 // close off the gST->ConIn
369 //
370 OldConIn = gST->ConIn;
371 ConInHandle = gST->ConsoleInHandle;
372 gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
373 } else {
374 OldConIn = NULL;
375 ConInHandle = NULL;
376 }
377
378 if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
379 //
380 // process the startup script or launch the called app.
381 //
382 Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
383 }
384
385 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
386 //
387 // begin the UI waiting loop
388 //
389 do {
390 //
391 // clean out all the memory allocated for CONST <something> * return values
392 // between each shell prompt presentation
393 //
394 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
395 FreeBufferList(&ShellInfoObject.BufferToFreeList);
396 }
397
398 //
399 // Reset page break back to default.
400 //
401 ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
402 ShellInfoObject.ConsoleInfo->Enabled = TRUE;
403 ShellInfoObject.ConsoleInfo->RowCounter = 0;
404
405 //
406 // Reset the CTRL-C event (yes we ignore the return values)
407 //
408 Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
409
410 //
411 // Display Prompt
412 //
413 Status = DoShellPrompt();
414 } while (!ShellCommandGetExit());
415 }
416 if (OldConIn != NULL && ConInHandle != NULL) {
417 CloseSimpleTextInOnFile (gST->ConIn);
418 gST->ConIn = OldConIn;
419 gST->ConsoleInHandle = ConInHandle;
420 }
421 }
422 }
423
424 //
425 // uninstall protocols / free memory / etc...
426 //
427 if (ShellInfoObject.UserBreakTimer != NULL) {
428 gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
429 DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
430 }
431 if (ShellInfoObject.ImageDevPath != NULL) {
432 FreePool(ShellInfoObject.ImageDevPath);
433 DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
434 }
435 if (ShellInfoObject.FileDevPath != NULL) {
436 FreePool(ShellInfoObject.FileDevPath);
437 DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
438 }
439 if (ShellInfoObject.NewShellParametersProtocol != NULL) {
440 CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
441 DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
442 }
443 if (ShellInfoObject.NewEfiShellProtocol != NULL){
444 if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
445 InternalEfiShellSetEnv(L"cwd", NULL, TRUE);
446 }
447 CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
448 DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
449 }
450
451 if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
452 FreeBufferList(&ShellInfoObject.BufferToFreeList);
453 }
454
455 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
456 ASSERT(FALSE); ///@todo finish this de-allocation.
457 }
458
459 if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
460 FreePool(ShellInfoObject.ShellInitSettings.FileName);
461 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
462 }
463
464 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
465 FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
466 DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
467 }
468
469 if (ShellInfoObject.HiiHandle != NULL) {
470 HiiRemovePackages(ShellInfoObject.HiiHandle);
471 DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
472 }
473
474 if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
475 FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
476 }
477
478 ASSERT(ShellInfoObject.ConsoleInfo != NULL);
479 if (ShellInfoObject.ConsoleInfo != NULL) {
480 ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
481 FreePool(ShellInfoObject.ConsoleInfo);
482 DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
483 }
484
485 if (ShellCommandGetExit()) {
486 return ((EFI_STATUS)ShellCommandGetExitCode());
487 }
488 return (Status);
489 }
490
491 /**
492 Sets all the alias' that were registered with the ShellCommandLib library.
493
494 @retval EFI_SUCCESS all init commands were run sucessfully.
495 **/
496 EFI_STATUS
497 EFIAPI
498 SetBuiltInAlias(
499 )
500 {
501 EFI_STATUS Status;
502 CONST ALIAS_LIST *List;
503 ALIAS_LIST *Node;
504
505 //
506 // Get all the commands we want to run
507 //
508 List = ShellCommandGetInitAliasList();
509
510 //
511 // for each command in the List
512 //
513 for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
514 ; !IsNull (&List->Link, &Node->Link)
515 ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
516 ){
517 //
518 // install the alias'
519 //
520 Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
521 ASSERT_EFI_ERROR(Status);
522 }
523 return (EFI_SUCCESS);
524 }
525
526 /**
527 Internal function to determine if 2 command names are really the same.
528
529 @param[in] Command1 The pointer to the first command name.
530 @param[in] Command2 The pointer to the second command name.
531
532 @retval TRUE The 2 command names are the same.
533 @retval FALSE The 2 command names are not the same.
534 **/
535 BOOLEAN
536 EFIAPI
537 IsCommand(
538 IN CONST CHAR16 *Command1,
539 IN CONST CHAR16 *Command2
540 )
541 {
542 if (StringNoCaseCompare(&Command1, &Command2) == 0) {
543 return (TRUE);
544 }
545 return (FALSE);
546 }
547
548 /**
549 Internal function to determine if a command is a script only command.
550
551 @param[in] CommandName The pointer to the command name.
552
553 @retval TRUE The command is a script only command.
554 @retval FALSE The command is not a script only command.
555 **/
556 BOOLEAN
557 EFIAPI
558 IsScriptOnlyCommand(
559 IN CONST CHAR16 *CommandName
560 )
561 {
562 if (IsCommand(CommandName, L"for")
563 ||IsCommand(CommandName, L"endfor")
564 ||IsCommand(CommandName, L"if")
565 ||IsCommand(CommandName, L"else")
566 ||IsCommand(CommandName, L"endif")
567 ||IsCommand(CommandName, L"goto")) {
568 return (TRUE);
569 }
570 return (FALSE);
571 }
572
573 /**
574 This function will populate the 2 device path protocol parameters based on the
575 global gImageHandle. The DevPath will point to the device path for the handle that has
576 loaded image protocol installed on it. The FilePath will point to the device path
577 for the file that was loaded.
578
579 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
580 @param[in, out] FilePath On a sucessful return the device path to the file.
581
582 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
583 @retval other A error from gBS->HandleProtocol.
584
585 @sa HandleProtocol
586 **/
587 EFI_STATUS
588 EFIAPI
589 GetDevicePathsForImageAndFile (
590 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
591 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
592 )
593 {
594 EFI_STATUS Status;
595 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
596 EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
597
598 ASSERT(DevPath != NULL);
599 ASSERT(FilePath != NULL);
600
601 Status = gBS->OpenProtocol (
602 gImageHandle,
603 &gEfiLoadedImageProtocolGuid,
604 (VOID**)&LoadedImage,
605 gImageHandle,
606 NULL,
607 EFI_OPEN_PROTOCOL_GET_PROTOCOL
608 );
609 if (!EFI_ERROR (Status)) {
610 Status = gBS->OpenProtocol (
611 LoadedImage->DeviceHandle,
612 &gEfiDevicePathProtocolGuid,
613 (VOID**)&ImageDevicePath,
614 gImageHandle,
615 NULL,
616 EFI_OPEN_PROTOCOL_GET_PROTOCOL
617 );
618 if (!EFI_ERROR (Status)) {
619 *DevPath = DuplicateDevicePath (ImageDevicePath);
620 *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
621 gBS->CloseProtocol(
622 LoadedImage->DeviceHandle,
623 &gEfiDevicePathProtocolGuid,
624 gImageHandle,
625 NULL);
626 }
627 gBS->CloseProtocol(
628 gImageHandle,
629 &gEfiLoadedImageProtocolGuid,
630 gImageHandle,
631 NULL);
632 }
633 return (Status);
634 }
635
636 STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
637 {L"-nostartup", TypeFlag},
638 {L"-startup", TypeFlag},
639 {L"-noconsoleout", TypeFlag},
640 {L"-noconsolein", TypeFlag},
641 {L"-nointerrupt", TypeFlag},
642 {L"-nomap", TypeFlag},
643 {L"-noversion", TypeFlag},
644 {L"-startup", TypeFlag},
645 {L"-delay", TypeValue},
646 {L"-_exit", TypeFlag},
647 {NULL, TypeMax}
648 };
649
650 /**
651 Process all Uefi Shell 2.0 command line options.
652
653 see Uefi Shell 2.0 section 3.2 for full details.
654
655 the command line must resemble the following:
656
657 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
658
659 ShellOpt-options Options which control the initialization behavior of the shell.
660 These options are read from the EFI global variable "ShellOpt"
661 and are processed before options or file-name.
662
663 options Options which control the initialization behavior of the shell.
664
665 file-name The name of a UEFI shell application or script to be executed
666 after initialization is complete. By default, if file-name is
667 specified, then -nostartup is implied. Scripts are not supported
668 by level 0.
669
670 file-name-options The command-line options that are passed to file-name when it
671 is invoked.
672
673 This will initialize the ShellInfoObject.ShellInitSettings global variable.
674
675 @retval EFI_SUCCESS The variable is initialized.
676 **/
677 EFI_STATUS
678 EFIAPI
679 ProcessCommandLine(
680 VOID
681 )
682 {
683 EFI_STATUS Status;
684 LIST_ENTRY *Package;
685 UINTN Size;
686 CONST CHAR16 *TempConst;
687 UINTN Count;
688 UINTN LoopVar;
689 CHAR16 *ProblemParam;
690 UINT64 Intermediate;
691
692 Package = NULL;
693 ProblemParam = NULL;
694
695 Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);
696
697 Count = 1;
698 Size = 0;
699 TempConst = ShellCommandLineGetRawValue(Package, Count++);
700 if (TempConst != NULL && StrLen(TempConst)) {
701 ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));
702 if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
703 return (EFI_OUT_OF_RESOURCES);
704 }
705 StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);
706 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
707 for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
708 if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {
709 LoopVar++;
710 //
711 // We found the file... add the rest of the params...
712 //
713 for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
714 ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
715 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
716 &Size,
717 L" ",
718 0);
719 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
720 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
721 return (EFI_OUT_OF_RESOURCES);
722 }
723 StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
724 &Size,
725 gEfiShellParametersProtocol->Argv[LoopVar],
726 0);
727 if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
728 SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
729 return (EFI_OUT_OF_RESOURCES);
730 }
731 }
732 }
733 }
734 } else {
735 ShellCommandLineFreeVarList(Package);
736 Package = NULL;
737 Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);
738 if (EFI_ERROR(Status)) {
739 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);
740 FreePool(ProblemParam);
741 ShellCommandLineFreeVarList(Package);
742 return (EFI_INVALID_PARAMETER);
743 }
744 }
745
746 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup");
747 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup");
748 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");
749 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein");
750 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt");
751 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap");
752 ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion");
753 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay");
754 ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = ShellCommandLineGetFlag(Package, L"-_exit");
755
756 ShellInfoObject.ShellInitSettings.Delay = 5;
757
758 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
759 ShellInfoObject.ShellInitSettings.Delay = 0;
760 } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {
761 TempConst = ShellCommandLineGetValue(Package, L"-delay");
762 if (TempConst != NULL && *TempConst == L':') {
763 TempConst++;
764 }
765 if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {
766 ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;
767 }
768 }
769 ShellCommandLineFreeVarList(Package);
770
771 return (Status);
772 }
773
774 /**
775 Handles all interaction with the default startup script.
776
777 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
778
779 @param ImagePath the path to the image for shell. first place to look for the startup script
780 @param FilePath the path to the file for shell. second place to look for the startup script.
781
782 @retval EFI_SUCCESS the variable is initialized.
783 **/
784 EFI_STATUS
785 EFIAPI
786 DoStartupScript(
787 EFI_DEVICE_PATH_PROTOCOL *ImagePath,
788 EFI_DEVICE_PATH_PROTOCOL *FilePath
789 )
790 {
791 EFI_STATUS Status;
792 UINTN Delay;
793 EFI_INPUT_KEY Key;
794 SHELL_FILE_HANDLE FileHandle;
795 EFI_DEVICE_PATH_PROTOCOL *NewPath;
796 EFI_DEVICE_PATH_PROTOCOL *NamePath;
797 CHAR16 *FileStringPath;
798 CHAR16 *TempSpot;
799 UINTN NewSize;
800 CONST CHAR16 *MapName;
801
802 Key.UnicodeChar = CHAR_NULL;
803 Key.ScanCode = 0;
804 FileHandle = NULL;
805
806 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
807 //
808 // launch something else instead
809 //
810 NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
811 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
812 NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
813 }
814 FileStringPath = AllocateZeroPool(NewSize);
815 if (FileStringPath == NULL) {
816 return (EFI_OUT_OF_RESOURCES);
817 }
818 StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);
819 if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
820 StrCat(FileStringPath, L" ");
821 StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);
822 }
823 Status = RunCommand(FileStringPath);
824 FreePool(FileStringPath);
825 return (Status);
826
827 }
828
829 //
830 // for shell level 0 we do no scripts
831 // Without the Startup bit overriding we allow for nostartup to prevent scripts
832 //
833 if ( (PcdGet8(PcdShellSupportLevel) < 1)
834 || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
835 ){
836 return (EFI_SUCCESS);
837 }
838
839 gST->ConOut->EnableCursor(gST->ConOut, FALSE);
840 //
841 // print out our warning and see if they press a key
842 //
843 for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay * 10
844 ; Delay != 0 && EFI_ERROR(Status)
845 ; Delay--
846 ){
847 ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);
848 gBS->Stall (100000);
849 if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
850 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
851 }
852 }
853 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
854 gST->ConOut->EnableCursor(gST->ConOut, TRUE);
855
856 //
857 // ESC was pressed
858 //
859 if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
860 return (EFI_SUCCESS);
861 }
862
863 //
864 // Try the first location (must be file system)
865 //
866 MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
867 if (MapName != NULL) {
868 FileStringPath = NULL;
869 NewSize = 0;
870 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
871 if (FileStringPath == NULL) {
872 Status = EFI_OUT_OF_RESOURCES;
873 } else {
874 TempSpot = StrStr(FileStringPath, L";");
875 if (TempSpot != NULL) {
876 *TempSpot = CHAR_NULL;
877 }
878 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
879 PathRemoveLastItem(FileStringPath);
880 FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
881 Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
882 FreePool(FileStringPath);
883 }
884 }
885 if (EFI_ERROR(Status)) {
886 NamePath = FileDevicePath (NULL, mStartupScript);
887 NewPath = AppendDevicePathNode (ImagePath, NamePath);
888 FreePool(NamePath);
889
890 //
891 // Try the location
892 //
893 Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
894 FreePool(NewPath);
895 }
896 //
897 // If we got a file, run it
898 //
899 if (!EFI_ERROR(Status) && FileHandle != NULL) {
900 Status = RunScriptFileHandle (FileHandle, mStartupScript);
901 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
902 } else {
903 FileStringPath = ShellFindFilePath(mStartupScript);
904 if (FileStringPath == NULL) {
905 //
906 // we return success since we dont need to have a startup script
907 //
908 Status = EFI_SUCCESS;
909 ASSERT(FileHandle == NULL);
910 } else {
911 Status = RunScriptFile(FileStringPath);
912 FreePool(FileStringPath);
913 }
914 }
915
916
917 return (Status);
918 }
919
920 /**
921 Function to perform the shell prompt looping. It will do a single prompt,
922 dispatch the result, and then return. It is expected that the caller will
923 call this function in a loop many times.
924
925 @retval EFI_SUCCESS
926 @retval RETURN_ABORTED
927 **/
928 EFI_STATUS
929 EFIAPI
930 DoShellPrompt (
931 VOID
932 )
933 {
934 UINTN Column;
935 UINTN Row;
936 CHAR16 *CmdLine;
937 CONST CHAR16 *CurDir;
938 UINTN BufferSize;
939 EFI_STATUS Status;
940
941 CurDir = NULL;
942
943 //
944 // Get screen setting to decide size of the command line buffer
945 //
946 gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
947 BufferSize = Column * Row * sizeof (CHAR16);
948 CmdLine = AllocateZeroPool (BufferSize);
949 if (CmdLine == NULL) {
950 return EFI_OUT_OF_RESOURCES;
951 }
952
953 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
954
955 //
956 // Prompt for input
957 //
958 gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
959
960 if (CurDir != NULL && StrLen(CurDir) > 1) {
961 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
962 } else {
963 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
964 }
965
966 //
967 // Read a line from the console
968 //
969 Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
970
971 //
972 // Null terminate the string and parse it
973 //
974 if (!EFI_ERROR (Status)) {
975 CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
976 Status = RunCommand(CmdLine);
977 }
978
979 //
980 // Done with this command
981 //
982 FreePool (CmdLine);
983 return Status;
984 }
985
986 /**
987 Add a buffer to the Buffer To Free List for safely returning buffers to other
988 places without risking letting them modify internal shell information.
989
990 @param Buffer Something to pass to FreePool when the shell is exiting.
991 **/
992 VOID*
993 EFIAPI
994 AddBufferToFreeList(
995 VOID *Buffer
996 )
997 {
998 BUFFER_LIST *BufferListEntry;
999
1000 if (Buffer == NULL) {
1001 return (NULL);
1002 }
1003
1004 BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
1005 ASSERT(BufferListEntry != NULL);
1006 BufferListEntry->Buffer = Buffer;
1007 InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
1008 return (Buffer);
1009 }
1010
1011 /**
1012 Add a buffer to the Line History List
1013
1014 @param Buffer The line buffer to add.
1015 **/
1016 VOID
1017 EFIAPI
1018 AddLineToCommandHistory(
1019 IN CONST CHAR16 *Buffer
1020 )
1021 {
1022 BUFFER_LIST *Node;
1023
1024 Node = AllocateZeroPool(sizeof(BUFFER_LIST));
1025 ASSERT(Node != NULL);
1026 Node->Buffer = AllocateZeroPool(StrSize(Buffer));
1027 ASSERT(Node->Buffer != NULL);
1028 StrCpy(Node->Buffer, Buffer);
1029
1030 InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
1031 }
1032
1033 /**
1034 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1035 with the correct command name.
1036
1037 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1038 command name if it was an alias. If it was not
1039 an alias it will be unchanged. This function may
1040 change the buffer to fit the command name.
1041
1042 @retval EFI_SUCCESS The name was changed.
1043 @retval EFI_SUCCESS The name was not an alias.
1044 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1045 **/
1046 EFI_STATUS
1047 EFIAPI
1048 ShellConvertAlias(
1049 IN OUT CHAR16 **CommandString
1050 )
1051 {
1052 CONST CHAR16 *NewString;
1053
1054 NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
1055 if (NewString == NULL) {
1056 return (EFI_SUCCESS);
1057 }
1058 FreePool(*CommandString);
1059 *CommandString = AllocateZeroPool(StrSize(NewString));
1060 if (*CommandString == NULL) {
1061 return (EFI_OUT_OF_RESOURCES);
1062 }
1063 StrCpy(*CommandString, NewString);
1064 return (EFI_SUCCESS);
1065 }
1066
1067 /**
1068 Function allocates a new command line and replaces all instances of environment
1069 variable names that are correctly preset to their values.
1070
1071 If the return value is not NULL the memory must be caller freed.
1072
1073 @param[in] OriginalCommandLine The original command line
1074
1075 @retval NULL An error ocurred.
1076 @return The new command line with no environment variables present.
1077 **/
1078 CHAR16*
1079 EFIAPI
1080 ShellConvertVariables (
1081 IN CONST CHAR16 *OriginalCommandLine
1082 )
1083 {
1084 CONST CHAR16 *MasterEnvList;
1085 UINTN NewSize;
1086 CHAR16 *NewCommandLine1;
1087 CHAR16 *NewCommandLine2;
1088 CHAR16 *Temp;
1089 CHAR16 *Temp2;
1090 UINTN ItemSize;
1091 CHAR16 *ItemTemp;
1092 SCRIPT_FILE *CurrentScriptFile;
1093 ALIAS_LIST *AliasListNode;
1094
1095 ASSERT(OriginalCommandLine != NULL);
1096
1097 ItemSize = 0;
1098 NewSize = StrSize(OriginalCommandLine);
1099 CurrentScriptFile = ShellCommandGetCurrentScriptFile();
1100 Temp = NULL;
1101
1102 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1103
1104 //
1105 // calculate the size required for the post-conversion string...
1106 //
1107 if (CurrentScriptFile != NULL) {
1108 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1109 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1110 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1111 ){
1112 for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
1113 ; Temp != NULL
1114 ; Temp = StrStr(Temp+1, AliasListNode->Alias)
1115 ){
1116 //
1117 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1118 //
1119 if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
1120 NewSize += StrSize(AliasListNode->CommandString);
1121 }
1122 }
1123 }
1124 }
1125
1126 for (MasterEnvList = EfiShellGetEnv(NULL)
1127 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1128 ; MasterEnvList += StrLen(MasterEnvList) + 1
1129 ){
1130 if (StrSize(MasterEnvList) > ItemSize) {
1131 ItemSize = StrSize(MasterEnvList);
1132 }
1133 for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
1134 ; Temp != NULL
1135 ; Temp = StrStr(Temp+1, MasterEnvList)
1136 ){
1137 //
1138 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1139 //
1140 if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
1141 ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
1142 NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
1143 }
1144 }
1145 }
1146
1147 //
1148 // now do the replacements...
1149 //
1150 NewCommandLine1 = AllocateZeroPool(NewSize);
1151 NewCommandLine2 = AllocateZeroPool(NewSize);
1152 ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
1153 if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
1154 SHELL_FREE_NON_NULL(NewCommandLine1);
1155 SHELL_FREE_NON_NULL(NewCommandLine2);
1156 SHELL_FREE_NON_NULL(ItemTemp);
1157 return (NULL);
1158 }
1159 StrCpy(NewCommandLine1, OriginalCommandLine);
1160 for (MasterEnvList = EfiShellGetEnv(NULL)
1161 ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
1162 ; MasterEnvList += StrLen(MasterEnvList) + 1
1163 ){
1164 StrCpy(ItemTemp, L"%");
1165 StrCat(ItemTemp, MasterEnvList);
1166 StrCat(ItemTemp, L"%");
1167 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
1168 StrCpy(NewCommandLine1, NewCommandLine2);
1169 }
1170 if (CurrentScriptFile != NULL) {
1171 for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
1172 ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1173 ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
1174 ){
1175 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
1176 StrCpy(NewCommandLine1, NewCommandLine2);
1177 }
1178
1179 //
1180 // Remove non-existant environment variables in scripts only
1181 //
1182 for (Temp = NewCommandLine1 ; Temp != NULL ; ) {
1183 Temp = StrStr(Temp, L"%");
1184 if (Temp == NULL) {
1185 break;
1186 }
1187 while (*(Temp - 1) == L'^') {
1188 Temp = StrStr(Temp + 1, L"%");
1189 if (Temp == NULL) {
1190 break;
1191 }
1192 }
1193 if (Temp == NULL) {
1194 break;
1195 }
1196
1197 Temp2 = StrStr(Temp + 1, L"%");
1198 if (Temp2 == NULL) {
1199 break;
1200 }
1201 while (*(Temp2 - 1) == L'^') {
1202 Temp2 = StrStr(Temp2 + 1, L"%");
1203 if (Temp2 == NULL) {
1204 break;
1205 }
1206 }
1207 if (Temp2 == NULL) {
1208 break;
1209 }
1210
1211 Temp2++;
1212 CopyMem(Temp, Temp2, StrSize(Temp2));
1213 }
1214
1215 }
1216
1217 //
1218 // Now cleanup any straggler intentionally ignored "%" characters
1219 //
1220 ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
1221 StrCpy(NewCommandLine1, NewCommandLine2);
1222
1223 FreePool(NewCommandLine2);
1224 FreePool(ItemTemp);
1225
1226 return (NewCommandLine1);
1227 }
1228
1229 /**
1230 Internal function to run a command line with pipe usage.
1231
1232 @param[in] CmdLine The pointer to the command line.
1233 @param[in] StdIn The pointer to the Standard input.
1234 @param[in] StdOut The pointer to the Standard output.
1235
1236 @retval EFI_SUCCESS The split command is executed successfully.
1237 @retval other Some error occurs when executing the split command.
1238 **/
1239 EFI_STATUS
1240 EFIAPI
1241 RunSplitCommand(
1242 IN CONST CHAR16 *CmdLine,
1243 IN SHELL_FILE_HANDLE *StdIn,
1244 IN SHELL_FILE_HANDLE *StdOut
1245 )
1246 {
1247 EFI_STATUS Status;
1248 CHAR16 *NextCommandLine;
1249 CHAR16 *OurCommandLine;
1250 UINTN Size1;
1251 UINTN Size2;
1252 SPLIT_LIST *Split;
1253 SHELL_FILE_HANDLE *TempFileHandle;
1254 BOOLEAN Unicode;
1255
1256 ASSERT(StdOut == NULL);
1257
1258 ASSERT(StrStr(CmdLine, L"|") != NULL);
1259
1260 Status = EFI_SUCCESS;
1261 NextCommandLine = NULL;
1262 OurCommandLine = NULL;
1263 Size1 = 0;
1264 Size2 = 0;
1265
1266 NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
1267 OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
1268
1269 if (NextCommandLine == NULL || OurCommandLine == NULL) {
1270 SHELL_FREE_NON_NULL(OurCommandLine);
1271 SHELL_FREE_NON_NULL(NextCommandLine);
1272 return (EFI_OUT_OF_RESOURCES);
1273 } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
1274 SHELL_FREE_NON_NULL(OurCommandLine);
1275 SHELL_FREE_NON_NULL(NextCommandLine);
1276 return (EFI_INVALID_PARAMETER);
1277 } else if (NextCommandLine[0] != CHAR_NULL &&
1278 NextCommandLine[0] == L'a' &&
1279 NextCommandLine[1] == L' '
1280 ){
1281 CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
1282 Unicode = FALSE;
1283 } else {
1284 Unicode = TRUE;
1285 }
1286
1287
1288 //
1289 // make a SPLIT_LIST item and add to list
1290 //
1291 Split = AllocateZeroPool(sizeof(SPLIT_LIST));
1292 ASSERT(Split != NULL);
1293 Split->SplitStdIn = StdIn;
1294 Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
1295 ASSERT(Split->SplitStdOut != NULL);
1296 InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
1297
1298 Status = RunCommand(OurCommandLine);
1299
1300 //
1301 // move the output from the first to the in to the second.
1302 //
1303 TempFileHandle = Split->SplitStdOut;
1304 if (Split->SplitStdIn == StdIn) {
1305 Split->SplitStdOut = NULL;
1306 } else {
1307 Split->SplitStdOut = Split->SplitStdIn;
1308 }
1309 Split->SplitStdIn = TempFileHandle;
1310 ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
1311
1312 if (!EFI_ERROR(Status)) {
1313 Status = RunCommand(NextCommandLine);
1314 }
1315
1316 //
1317 // remove the top level from the ScriptList
1318 //
1319 ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
1320 RemoveEntryList(&Split->Link);
1321
1322 //
1323 // Note that the original StdIn is now the StdOut...
1324 //
1325 if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
1326 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
1327 }
1328 if (Split->SplitStdIn != NULL) {
1329 ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
1330 }
1331
1332 FreePool(Split);
1333 FreePool(NextCommandLine);
1334 FreePool(OurCommandLine);
1335
1336 return (Status);
1337 }
1338
1339 /**
1340 Function will process and run a command line.
1341
1342 This will determine if the command line represents an internal shell
1343 command or dispatch an external application.
1344
1345 @param[in] CmdLine The command line to parse.
1346
1347 @retval EFI_SUCCESS The command was completed.
1348 @retval EFI_ABORTED The command's operation was aborted.
1349 **/
1350 EFI_STATUS
1351 EFIAPI
1352 RunCommand(
1353 IN CONST CHAR16 *CmdLine
1354 )
1355 {
1356 EFI_STATUS Status;
1357 EFI_STATUS StatusCode;
1358 CHAR16 *CommandName;
1359 SHELL_STATUS ShellStatus;
1360 UINTN Argc;
1361 CHAR16 **Argv;
1362 BOOLEAN LastError;
1363 CHAR16 LeString[19];
1364 CHAR16 *PostAliasCmdLine;
1365 UINTN PostAliasSize;
1366 CHAR16 *PostVariableCmdLine;
1367 CHAR16 *CommandWithPath;
1368 CONST EFI_DEVICE_PATH_PROTOCOL *DevPath;
1369 CONST CHAR16 *TempLocation;
1370 CONST CHAR16 *TempLocation2;
1371 SHELL_FILE_HANDLE OriginalStdIn;
1372 SHELL_FILE_HANDLE OriginalStdOut;
1373 SHELL_FILE_HANDLE OriginalStdErr;
1374 SYSTEM_TABLE_INFO OriginalSystemTableInfo;
1375 CHAR16 *TempLocation3;
1376 UINTN Count;
1377 UINTN Count2;
1378 CHAR16 *CleanOriginal;
1379 SPLIT_LIST *Split;
1380
1381 ASSERT(CmdLine != NULL);
1382 if (StrLen(CmdLine) == 0) {
1383 return (EFI_SUCCESS);
1384 }
1385
1386 CommandName = NULL;
1387 PostVariableCmdLine = NULL;
1388 PostAliasCmdLine = NULL;
1389 CommandWithPath = NULL;
1390 DevPath = NULL;
1391 Status = EFI_SUCCESS;
1392 CleanOriginal = NULL;
1393 Split = NULL;
1394
1395 CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
1396 if (CleanOriginal == NULL) {
1397 return (EFI_OUT_OF_RESOURCES);
1398 }
1399 while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {
1400 CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;
1401 }
1402 while (CleanOriginal[0] == L' ') {
1403 CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));
1404 }
1405
1406 CommandName = NULL;
1407 if (StrStr(CleanOriginal, L" ") == NULL){
1408 StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);
1409 } else {
1410 StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);
1411 }
1412
1413 ASSERT(PostAliasCmdLine == NULL);
1414 if (!ShellCommandIsCommandOnList(CommandName)) {
1415 //
1416 // Convert via alias
1417 //
1418 Status = ShellConvertAlias(&CommandName);
1419 PostAliasSize = 0;
1420 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);
1421 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);
1422 ASSERT_EFI_ERROR(Status);
1423 } else {
1424 PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);
1425 }
1426
1427 if (CleanOriginal != NULL) {
1428 FreePool(CleanOriginal);
1429 CleanOriginal = NULL;
1430 }
1431
1432 if (CommandName != NULL) {
1433 FreePool(CommandName);
1434 CommandName = NULL;
1435 }
1436
1437 PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);
1438
1439 //
1440 // we can now free the modified by alias command line
1441 //
1442 if (PostAliasCmdLine != NULL) {
1443 FreePool(PostAliasCmdLine);
1444 PostAliasCmdLine = NULL;
1445 }
1446
1447 if (PostVariableCmdLine == NULL) {
1448 return (EFI_OUT_OF_RESOURCES);
1449 }
1450
1451 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1452 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1453 }
1454 while (PostVariableCmdLine[0] == L' ') {
1455 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1456 }
1457
1458 //
1459 // We dont do normal processing with a split command line (output from one command input to another)
1460 //
1461 TempLocation3 = NULL;
1462 if (StrStr(PostVariableCmdLine, L"|") != NULL) {
1463 for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {
1464 if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {
1465 TempLocation3++;
1466 } else if (*TempLocation3 == L'|') {
1467 break;
1468 }
1469 }
1470 }
1471 if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {
1472 //
1473 // are we in an existing split???
1474 //
1475 if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
1476 Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
1477 }
1478
1479 if (Split == NULL) {
1480 Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);
1481 } else {
1482 Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);
1483 }
1484 if (EFI_ERROR(Status)) {
1485 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1486 }
1487 } else {
1488
1489 //
1490 // If this is a mapped drive change handle that...
1491 //
1492 if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {
1493 Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);
1494 if (EFI_ERROR(Status)) {
1495 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);
1496 }
1497 FreePool(PostVariableCmdLine);
1498 return (Status);
1499 }
1500
1501 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1502 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1503
1504
1505
1506 Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1507 if (EFI_ERROR(Status)) {
1508 if (Status == EFI_NOT_FOUND) {
1509 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
1510 } else {
1511 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
1512 }
1513 } else {
1514 while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {
1515 PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;
1516 }
1517 while (PostVariableCmdLine[0] == L' ') {
1518 CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));
1519 }
1520
1521 //
1522 // get the argc and argv updated for internal commands
1523 //
1524 Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);
1525 ASSERT_EFI_ERROR(Status);
1526
1527 for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {
1528 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]
1529 || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)
1530 ) {
1531 //
1532 // We need to redo the arguments since a parameter was -?
1533 // move them all down 1 to the end, then up one then replace the first with help
1534 //
1535 FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);
1536 ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;
1537 for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {
1538 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];
1539 }
1540 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;
1541 for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {
1542 ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];
1543 }
1544 ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;
1545 ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);
1546 break;
1547 }
1548 }
1549
1550 //
1551 // command or file?
1552 //
1553 if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1554 //
1555 // Run the command (which was converted if it was an alias)
1556 //
1557 if (!EFI_ERROR(Status)) {
1558 Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);
1559 ASSERT_EFI_ERROR(Status);
1560 UnicodeSPrint(LeString, sizeof(LeString), L"0x%08Lx", ShellStatus);
1561 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1562 if (LastError) {
1563 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1564 }
1565 //
1566 // Pass thru the exitcode from the app.
1567 //
1568 if (ShellCommandGetExit()) {
1569 Status = ShellStatus;
1570 } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {
1571 Status = EFI_ABORTED;
1572 }
1573 }
1574 } else {
1575 //
1576 // run an external file (or script)
1577 //
1578 if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {
1579 ASSERT (CommandWithPath == NULL);
1580 if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {
1581 CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1582 }
1583 }
1584 if (CommandWithPath == NULL) {
1585 CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);
1586 }
1587 if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {
1588 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);
1589 } else {
1590 //
1591 // Check if it's a NSH (script) file.
1592 //
1593 TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;
1594 TempLocation2 = mScriptExtension;
1595 if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {
1596 Status = RunScriptFile (CommandWithPath);
1597 } else {
1598 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
1599 ASSERT(DevPath != NULL);
1600 Status = InternalShellExecuteDevicePath(
1601 &gImageHandle,
1602 DevPath,
1603 PostVariableCmdLine,
1604 NULL,
1605 &StatusCode
1606 );
1607
1608 //
1609 // Updatet last error status.
1610 //
1611 UnicodeSPrint(LeString, sizeof(LeString), L"0x%08Lx", StatusCode);
1612 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1613 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1614 }
1615 }
1616 }
1617
1618 //
1619 // Print some error info.
1620 //
1621 if (EFI_ERROR(Status)) {
1622 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
1623 }
1624
1625 CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);
1626
1627 RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);
1628
1629 RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
1630 }
1631 if (CommandName != NULL) {
1632 if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) {
1633 //
1634 // if this is NOT a scipt only command return success so the script won't quit.
1635 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1636 //
1637 Status = EFI_SUCCESS;
1638 }
1639 }
1640 }
1641
1642 SHELL_FREE_NON_NULL(CommandName);
1643 SHELL_FREE_NON_NULL(CommandWithPath);
1644 SHELL_FREE_NON_NULL(PostVariableCmdLine);
1645
1646 return (Status);
1647 }
1648
1649 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
1650 /**
1651 Function determins if the CommandName COULD be a valid command. It does not determine whether
1652 this is a valid command. It only checks for invalid characters.
1653
1654 @param[in] CommandName The name to check
1655
1656 @retval TRUE CommandName could be a command name
1657 @retval FALSE CommandName could not be a valid command name
1658 **/
1659 BOOLEAN
1660 EFIAPI
1661 IsValidCommandName(
1662 IN CONST CHAR16 *CommandName
1663 )
1664 {
1665 UINTN Count;
1666 if (CommandName == NULL) {
1667 ASSERT(FALSE);
1668 return (FALSE);
1669 }
1670 for ( Count = 0
1671 ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
1672 ; Count++
1673 ){
1674 if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
1675 return (FALSE);
1676 }
1677 }
1678 return (TRUE);
1679 }
1680
1681 /**
1682 Function to process a NSH script file via SHELL_FILE_HANDLE.
1683
1684 @param[in] Handle The handle to the already opened file.
1685 @param[in] Name The name of the script file.
1686
1687 @retval EFI_SUCCESS the script completed sucessfully
1688 **/
1689 EFI_STATUS
1690 EFIAPI
1691 RunScriptFileHandle (
1692 IN SHELL_FILE_HANDLE Handle,
1693 IN CONST CHAR16 *Name
1694 )
1695 {
1696 EFI_STATUS Status;
1697 SCRIPT_FILE *NewScriptFile;
1698 UINTN LoopVar;
1699 CHAR16 *CommandLine;
1700 CHAR16 *CommandLine2;
1701 CHAR16 *CommandLine3;
1702 SCRIPT_COMMAND_LIST *LastCommand;
1703 BOOLEAN Ascii;
1704 BOOLEAN PreScriptEchoState;
1705 BOOLEAN PreCommandEchoState;
1706 CONST CHAR16 *CurDir;
1707 UINTN LineCount;
1708 CHAR16 LeString[50];
1709
1710 ASSERT(!ShellCommandGetScriptExit());
1711
1712 PreScriptEchoState = ShellCommandGetEchoState();
1713
1714 NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
1715 if (NewScriptFile == NULL) {
1716 return (EFI_OUT_OF_RESOURCES);
1717 }
1718
1719 //
1720 // Set up the name
1721 //
1722 ASSERT(NewScriptFile->ScriptName == NULL);
1723 NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
1724 if (NewScriptFile->ScriptName == NULL) {
1725 DeleteScriptFileStruct(NewScriptFile);
1726 return (EFI_OUT_OF_RESOURCES);
1727 }
1728
1729 //
1730 // Save the parameters (used to replace %0 to %9 later on)
1731 //
1732 NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
1733 if (NewScriptFile->Argc != 0) {
1734 NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
1735 if (NewScriptFile->Argv == NULL) {
1736 DeleteScriptFileStruct(NewScriptFile);
1737 return (EFI_OUT_OF_RESOURCES);
1738 }
1739 for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
1740 ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
1741 NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
1742 if (NewScriptFile->Argv[LoopVar] == NULL) {
1743 DeleteScriptFileStruct(NewScriptFile);
1744 return (EFI_OUT_OF_RESOURCES);
1745 }
1746 }
1747 } else {
1748 NewScriptFile->Argv = NULL;
1749 }
1750
1751 InitializeListHead(&NewScriptFile->CommandList);
1752 InitializeListHead(&NewScriptFile->SubstList);
1753
1754 //
1755 // Now build the list of all script commands.
1756 //
1757 LineCount = 0;
1758 while(!ShellFileHandleEof(Handle)) {
1759 CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
1760 LineCount++;
1761 if (CommandLine == NULL || StrLen(CommandLine) == 0) {
1762 continue;
1763 }
1764 NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
1765 if (NewScriptFile->CurrentCommand == NULL) {
1766 DeleteScriptFileStruct(NewScriptFile);
1767 return (EFI_OUT_OF_RESOURCES);
1768 }
1769
1770 NewScriptFile->CurrentCommand->Cl = CommandLine;
1771 NewScriptFile->CurrentCommand->Data = NULL;
1772 NewScriptFile->CurrentCommand->Line = LineCount;
1773
1774 InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1775 }
1776
1777 //
1778 // Add this as the topmost script file
1779 //
1780 ShellCommandSetNewScript (NewScriptFile);
1781
1782 //
1783 // Now enumerate through the commands and run each one.
1784 //
1785 CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1786 if (CommandLine == NULL) {
1787 DeleteScriptFileStruct(NewScriptFile);
1788 return (EFI_OUT_OF_RESOURCES);
1789 }
1790 CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));
1791 if (CommandLine2 == NULL) {
1792 FreePool(CommandLine);
1793 DeleteScriptFileStruct(NewScriptFile);
1794 return (EFI_OUT_OF_RESOURCES);
1795 }
1796
1797 for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
1798 ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
1799 ; // conditional increment in the body of the loop
1800 ){
1801 ASSERT(CommandLine2 != NULL);
1802 StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);
1803
1804 //
1805 // NULL out comments
1806 //
1807 for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
1808 if (*CommandLine3 == L'^') {
1809 if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {
1810 CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
1811 }
1812 } else if (*CommandLine3 == L'#') {
1813 *CommandLine3 = CHAR_NULL;
1814 }
1815 }
1816
1817 if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
1818 //
1819 // Due to variability in starting the find and replace action we need to have both buffers the same.
1820 //
1821 StrCpy(CommandLine, CommandLine2);
1822
1823 //
1824 // Remove the %0 to %9 from the command line (if we have some arguments)
1825 //
1826 if (NewScriptFile->Argv != NULL) {
1827 switch (NewScriptFile->Argc) {
1828 default:
1829 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);
1830 ASSERT_EFI_ERROR(Status);
1831 case 9:
1832 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);
1833 ASSERT_EFI_ERROR(Status);
1834 case 8:
1835 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);
1836 ASSERT_EFI_ERROR(Status);
1837 case 7:
1838 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);
1839 ASSERT_EFI_ERROR(Status);
1840 case 6:
1841 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);
1842 ASSERT_EFI_ERROR(Status);
1843 case 5:
1844 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);
1845 ASSERT_EFI_ERROR(Status);
1846 case 4:
1847 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);
1848 ASSERT_EFI_ERROR(Status);
1849 case 3:
1850 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);
1851 ASSERT_EFI_ERROR(Status);
1852 case 2:
1853 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);
1854 ASSERT_EFI_ERROR(Status);
1855 case 1:
1856 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);
1857 ASSERT_EFI_ERROR(Status);
1858 break;
1859 case 0:
1860 break;
1861 }
1862 }
1863 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);
1864 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);
1865 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);
1866 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);
1867 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);
1868 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);
1869 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);
1870 Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);
1871 Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);
1872
1873 StrCpy(CommandLine2, CommandLine);
1874
1875 LastCommand = NewScriptFile->CurrentCommand;
1876
1877 for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
1878
1879 if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
1880 //
1881 // This line is a goto target / label
1882 //
1883 } else {
1884 if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
1885 if (CommandLine3[0] == L'@') {
1886 //
1887 // We need to save the current echo state
1888 // and disable echo for just this command.
1889 //
1890 PreCommandEchoState = ShellCommandGetEchoState();
1891 ShellCommandSetEchoState(FALSE);
1892 Status = RunCommand(CommandLine3+1);
1893
1894 //
1895 // Now restore the pre-'@' echo state.
1896 //
1897 ShellCommandSetEchoState(PreCommandEchoState);
1898 } else {
1899 if (ShellCommandGetEchoState()) {
1900 CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
1901 if (CurDir != NULL && StrLen(CurDir) > 1) {
1902 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
1903 } else {
1904 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
1905 }
1906 ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
1907 }
1908 Status = RunCommand(CommandLine3);
1909 }
1910 }
1911
1912 if (ShellCommandGetScriptExit()) {
1913 UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
1914 DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););
1915 InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);
1916
1917 ShellCommandRegisterExit(FALSE, 0);
1918 Status = EFI_SUCCESS;
1919 break;
1920 }
1921 if (ShellGetExecutionBreakFlag()) {
1922 break;
1923 }
1924 if (EFI_ERROR(Status)) {
1925 break;
1926 }
1927 if (ShellCommandGetExit()) {
1928 break;
1929 }
1930 }
1931 //
1932 // If that commend did not update the CurrentCommand then we need to advance it...
1933 //
1934 if (LastCommand == NewScriptFile->CurrentCommand) {
1935 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1936 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1937 NewScriptFile->CurrentCommand->Reset = TRUE;
1938 }
1939 }
1940 } else {
1941 NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
1942 if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
1943 NewScriptFile->CurrentCommand->Reset = TRUE;
1944 }
1945 }
1946 }
1947
1948
1949 FreePool(CommandLine);
1950 FreePool(CommandLine2);
1951 ShellCommandSetNewScript (NULL);
1952
1953 //
1954 // Only if this was the last script reset the state.
1955 //
1956 if (ShellCommandGetCurrentScriptFile()==NULL) {
1957 ShellCommandSetEchoState(PreScriptEchoState);
1958 }
1959 return (EFI_SUCCESS);
1960 }
1961
1962 /**
1963 Function to process a NSH script file.
1964
1965 @param[in] ScriptPath Pointer to the script file name (including file system path).
1966
1967 @retval EFI_SUCCESS the script completed sucessfully
1968 **/
1969 EFI_STATUS
1970 EFIAPI
1971 RunScriptFile (
1972 IN CONST CHAR16 *ScriptPath
1973 )
1974 {
1975 EFI_STATUS Status;
1976 SHELL_FILE_HANDLE FileHandle;
1977
1978 if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
1979 return (EFI_INVALID_PARAMETER);
1980 }
1981
1982 Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
1983 if (EFI_ERROR(Status)) {
1984 return (Status);
1985 }
1986
1987 Status = RunScriptFileHandle(FileHandle, ScriptPath);
1988
1989 ShellCloseFile(&FileHandle);
1990
1991 return (Status);
1992 }