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