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