2 Main file for endfor and for shell level 1 functions.
4 Copyright (c) 2009 - 2010, 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
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.
15 #include "UefiShellLevel1CommandsLib.h"
16 #include <Library/PrintLib.h>
20 ShellIsValidForNumber (
21 IN CONST CHAR16
*Number
24 if (Number
== NULL
|| *Number
== CHAR_NULL
) {
28 if (*Number
== L
'-') {
32 if (StrLen(Number
) == 0) {
36 if (StrLen(Number
) >= 7) {
37 if (StrStr(Number
, L
" ") != NULL
&& (StrStr(Number
, L
" ") - Number
) >= 7) {
42 if (!ShellIsDecimalDigitCharacter(*Number
)) {
50 Function for 'endfor' command.
52 @param[in] ImageHandle Handle to the Image (NULL if Internal).
53 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
57 ShellCommandRunEndFor (
58 IN EFI_HANDLE ImageHandle
,
59 IN EFI_SYSTEM_TABLE
*SystemTable
65 Status
= CommandInit();
66 ASSERT_EFI_ERROR(Status
);
68 if (!gEfiShellProtocol
->BatchIsActive()) {
69 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_NO_SCRIPT
), gShellLevel1HiiHandle
, L
"EndFor");
70 return (SHELL_UNSUPPORTED
);
73 if (gEfiShellParametersProtocol
->Argc
> 1) {
74 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_MANY
), gShellLevel1HiiHandle
);
75 return (SHELL_INVALID_PARAMETER
);
78 Found
= MoveToTag(GetPreviousNode
, L
"for", L
"endfor", NULL
, ShellCommandGetCurrentScriptFile(), FALSE
, FALSE
, FALSE
);
81 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SYNTAX_NO_MATCHING
), gShellLevel1HiiHandle
, L
"For", L
"EndFor", ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
82 return (SHELL_NOT_FOUND
);
84 return (SHELL_SUCCESS
);
92 CHAR16
*ReplacementName
;
94 BOOLEAN RemoveSubstAlias
;
97 #define SIZE_OF_SHELL_FOR_INFO OFFSET_OF (SHELL_FOR_INFO, Set)
98 #define SHELL_FOR_INFO_SIGNATURE SIGNATURE_32 ('S', 'F', 'I', 's')
101 Update the value of a given alias on the list. If the alias is not there then add it.
103 @param[in] Alias The alias to test for.
104 @param[in] CommandString The updated command string.
105 @param[in,out] List The list to search.
107 @retval EFI_SUCCESS The operation was completed successfully.
108 @retval EFI_OUT_OF_RESOURCES There was not enough free memory.
112 InternalUpdateAliasOnList(
113 IN CONST CHAR16
*Alias
,
114 IN CONST CHAR16
*CommandString
,
115 IN OUT LIST_ENTRY
*List
122 // assert for NULL parameter
124 ASSERT(Alias
!= NULL
);
127 // check for the Alias
129 for ( Node
= (ALIAS_LIST
*)GetFirstNode(List
), Found
= FALSE
130 ; !IsNull(List
, &Node
->Link
)
131 ; Node
= (ALIAS_LIST
*)GetNextNode(List
, &Node
->Link
)
133 ASSERT(Node
->CommandString
!= NULL
);
134 ASSERT(Node
->Alias
!= NULL
);
135 if (StrCmp(Node
->Alias
, Alias
)==0) {
136 FreePool(Node
->CommandString
);
137 Node
->CommandString
= NULL
;
138 Node
->CommandString
= StrnCatGrow(&Node
->CommandString
, NULL
, CommandString
, 0);
144 Node
= AllocateZeroPool(sizeof(ALIAS_LIST
));
146 return (EFI_OUT_OF_RESOURCES
);
148 ASSERT(Node
->Alias
== NULL
);
149 Node
->Alias
= StrnCatGrow(&Node
->Alias
, NULL
, Alias
, 0);
150 ASSERT(Node
->CommandString
== NULL
);
151 Node
->CommandString
= StrnCatGrow(&Node
->CommandString
, NULL
, CommandString
, 0);
152 InsertTailList(List
, &Node
->Link
);
154 return (EFI_SUCCESS
);
158 Find out if an alias is on the given list.
160 @param[in] Alias The alias to test for.
161 @param[in] List The list to search.
163 @retval TRUE The alias is on the list.
164 @retval FALSE The alias is not on the list.
168 InternalIsAliasOnList(
169 IN CONST CHAR16
*Alias
,
170 IN CONST LIST_ENTRY
*List
176 // assert for NULL parameter
178 ASSERT(Alias
!= NULL
);
181 // check for the Alias
183 for ( Node
= (ALIAS_LIST
*)GetFirstNode(List
)
184 ; !IsNull(List
, &Node
->Link
)
185 ; Node
= (ALIAS_LIST
*)GetNextNode(List
, &Node
->Link
)
187 ASSERT(Node
->CommandString
!= NULL
);
188 ASSERT(Node
->Alias
!= NULL
);
189 if (StrCmp(Node
->Alias
, Alias
)==0) {
197 Remove an alias from the given list.
199 @param[in] Alias The alias to remove.
200 @param[in,out] List The list to search.
204 InternalRemoveAliasFromList(
205 IN CONST CHAR16
*Alias
,
206 IN OUT LIST_ENTRY
*List
212 // assert for NULL parameter
214 ASSERT(Alias
!= NULL
);
217 // check for the Alias
219 for ( Node
= (ALIAS_LIST
*)GetFirstNode(List
)
220 ; !IsNull(List
, &Node
->Link
)
221 ; Node
= (ALIAS_LIST
*)GetNextNode(List
, &Node
->Link
)
223 ASSERT(Node
->CommandString
!= NULL
);
224 ASSERT(Node
->Alias
!= NULL
);
225 if (StrCmp(Node
->Alias
, Alias
)==0) {
226 RemoveEntryList(&Node
->Link
);
227 FreePool(Node
->Alias
);
228 FreePool(Node
->CommandString
);
237 Function for 'for' command.
239 @param[in] ImageHandle Handle to the Image (NULL if Internal).
240 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
245 IN EFI_HANDLE ImageHandle
,
246 IN EFI_SYSTEM_TABLE
*SystemTable
250 SHELL_STATUS ShellStatus
;
251 SCRIPT_FILE
*CurrentScriptFile
;
253 CHAR16
*ArgSetWalker
;
256 SHELL_FOR_INFO
*Info
;
260 EFI_SHELL_FILE_INFO
*Node
;
261 EFI_SHELL_FILE_INFO
*FileList
;
266 ShellStatus
= SHELL_SUCCESS
;
272 // initialize the shell lib (we must be in non-auto-init...)
274 Status
= ShellInitialize();
275 ASSERT_EFI_ERROR(Status
);
277 Status
= CommandInit();
278 ASSERT_EFI_ERROR(Status
);
280 if (!gEfiShellProtocol
->BatchIsActive()) {
281 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_NO_SCRIPT
), gShellLevel1HiiHandle
, L
"For");
282 return (SHELL_UNSUPPORTED
);
285 if (gEfiShellParametersProtocol
->Argc
< 4) {
286 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_FEW
), gShellLevel1HiiHandle
);
287 return (SHELL_INVALID_PARAMETER
);
290 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
291 ASSERT(CurrentScriptFile
!= NULL
);
293 if (CurrentScriptFile
->CurrentCommand
->Data
== NULL
) {
297 // Make sure that an End exists.
299 if (!MoveToTag(GetNextNode
, L
"endfor", L
"for", NULL
, CurrentScriptFile
, TRUE
, TRUE
, FALSE
)) {
300 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SYNTAX_NO_MATCHING
), gShellLevel1HiiHandle
, L
"EndFor", L
"For", ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
301 return (SHELL_DEVICE_ERROR
);
307 if (gEfiShellParametersProtocol
->Argv
[1][0] != L
'%' || gEfiShellParametersProtocol
->Argv
[1][2] != CHAR_NULL
308 ||!((gEfiShellParametersProtocol
->Argv
[1][1] >= L
'a' && gEfiShellParametersProtocol
->Argv
[1][1] <= L
'z')
309 ||(gEfiShellParametersProtocol
->Argv
[1][1] >= L
'A' && gEfiShellParametersProtocol
->Argv
[1][1] <= L
'Z'))
311 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_INV_VAR
), gShellLevel1HiiHandle
, gEfiShellParametersProtocol
->Argv
[2]);
312 return (SHELL_INVALID_PARAMETER
);
315 if (gUnicodeCollation
->StriColl(
318 gEfiShellParametersProtocol
->Argv
[2]) == 0) {
319 for (LoopVar
= 0x3 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
320 ASSERT((ArgSet
== NULL
&& ArgSize
== 0) || (ArgSet
!= NULL
));
321 if (ArgSet
== NULL
) {
322 // ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
324 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, L
" \"", 0);
326 if (StrStr(gEfiShellParametersProtocol
->Argv
[LoopVar
], L
"*") != NULL
327 ||StrStr(gEfiShellParametersProtocol
->Argv
[LoopVar
], L
"?") != NULL
328 ||StrStr(gEfiShellParametersProtocol
->Argv
[LoopVar
], L
"[") != NULL
329 ||StrStr(gEfiShellParametersProtocol
->Argv
[LoopVar
], L
"]") != NULL
) {
331 Status
= ShellOpenFileMetaArg ((CHAR16
*)gEfiShellParametersProtocol
->Argv
[LoopVar
], EFI_FILE_MODE_READ
, &FileList
);
332 if (EFI_ERROR(Status
) || FileList
== NULL
|| IsListEmpty(&FileList
->Link
)) {
333 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, gEfiShellParametersProtocol
->Argv
[LoopVar
], 0);
335 for (Node
= (EFI_SHELL_FILE_INFO
*)GetFirstNode(&FileList
->Link
)
336 ; !IsNull(&FileList
->Link
, &Node
->Link
)
337 ; Node
= (EFI_SHELL_FILE_INFO
*)GetNextNode(&FileList
->Link
, &Node
->Link
)
339 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, L
" \"", 0);
340 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, Node
->FullName
, 0);
341 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, L
"\"", 0);
343 ShellCloseFileMetaArg(&FileList
);
346 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, gEfiShellParametersProtocol
->Argv
[LoopVar
], 0);
348 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, L
"\"", 0);
351 // set up for an 'in' for loop
353 NewSize
= StrSize(ArgSet
);
354 NewSize
+= sizeof(SHELL_FOR_INFO
)+StrSize(gEfiShellParametersProtocol
->Argv
[1]);
355 Info
= AllocateZeroPool(NewSize
);
356 ASSERT(Info
!= NULL
);
357 Info
->Signature
= SHELL_FOR_INFO_SIGNATURE
;
358 CopyMem(Info
->Set
, ArgSet
, StrSize(ArgSet
));
359 NewSize
= StrSize(gEfiShellParametersProtocol
->Argv
[1]);
360 CopyMem(Info
->Set
+(StrSize(ArgSet
)/sizeof(Info
->Set
[0])), gEfiShellParametersProtocol
->Argv
[1], NewSize
);
361 Info
->ReplacementName
= Info
->Set
+StrSize(ArgSet
)/sizeof(Info
->Set
[0]);
362 Info
->CurrentValue
= (CHAR16
*)Info
->Set
;
367 if (InternalIsAliasOnList(Info
->ReplacementName
, &CurrentScriptFile
->SubstList
)) {
368 Info
->RemoveSubstAlias
= FALSE
;
370 Info
->RemoveSubstAlias
= TRUE
;
372 CurrentScriptFile
->CurrentCommand
->Data
= Info
;
373 } else if (gUnicodeCollation
->StriColl(
376 gEfiShellParametersProtocol
->Argv
[2]) == 0) {
377 for (LoopVar
= 0x3 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
378 ASSERT((ArgSet
== NULL
&& ArgSize
== 0) || (ArgSet
!= NULL
));
379 if (ArgSet
== NULL
) {
380 // ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
382 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, L
" ", 0);
384 ArgSet
= StrnCatGrow(&ArgSet
, &ArgSize
, gEfiShellParametersProtocol
->Argv
[LoopVar
], 0);
385 // ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
388 // set up for a 'run' for loop
390 Info
= AllocateZeroPool(sizeof(SHELL_FOR_INFO
)+StrSize(gEfiShellParametersProtocol
->Argv
[1]));
391 ASSERT(Info
!= NULL
);
392 CopyMem(Info
->Set
, gEfiShellParametersProtocol
->Argv
[1], StrSize(gEfiShellParametersProtocol
->Argv
[1]));
393 Info
->ReplacementName
= Info
->Set
;
394 Info
->CurrentValue
= NULL
;
395 ArgSetWalker
= ArgSet
;
396 if (ArgSetWalker
[0] != L
'(') {
397 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
398 ShellStatus
= SHELL_INVALID_PARAMETER
;
401 while (ArgSetWalker
!= NULL
&& ArgSetWalker
[0] == L
' ') {
404 if (!ShellIsValidForNumber(ArgSetWalker
)) {
405 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
406 ShellStatus
= SHELL_INVALID_PARAMETER
;
408 if (ArgSetWalker
[0] == L
'-') {
409 Info
->Current
= 0 - (INTN
)ShellStrToUintn(ArgSetWalker
+1);
411 Info
->Current
= (INTN
)ShellStrToUintn(ArgSetWalker
);
413 ArgSetWalker
= StrStr(ArgSetWalker
, L
" ");
414 while (ArgSetWalker
!= NULL
&& ArgSetWalker
[0] == L
' ') {
417 if (ArgSetWalker
== NULL
|| *ArgSetWalker
== CHAR_NULL
|| !ShellIsValidForNumber(ArgSetWalker
)){
418 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
419 ShellStatus
= SHELL_INVALID_PARAMETER
;
421 if (ArgSetWalker
[0] == L
'-') {
422 Info
->End
= 0 - (INTN
)ShellStrToUintn(ArgSetWalker
+1);
424 Info
->End
= (INTN
)ShellStrToUintn(ArgSetWalker
);
426 if (Info
->Current
< Info
->End
) {
432 ArgSetWalker
= StrStr(ArgSetWalker
, L
" ");
433 while (ArgSetWalker
!= NULL
&& ArgSetWalker
[0] == L
' ') {
436 if (ArgSetWalker
!= NULL
&& *ArgSetWalker
!= CHAR_NULL
) {
437 TempSpot
= StrStr(ArgSetWalker
, L
")");
438 if (TempSpot
== NULL
) {
439 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
440 ShellStatus
= SHELL_INVALID_PARAMETER
;
442 *TempSpot
= CHAR_NULL
;
443 if (ArgSetWalker
== NULL
|| *ArgSetWalker
== CHAR_NULL
|| !ShellIsValidForNumber(ArgSetWalker
)){
444 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
445 ShellStatus
= SHELL_INVALID_PARAMETER
;
447 if (*ArgSetWalker
== L
')') {
448 ASSERT(Info
->Step
== 1 || Info
->Step
== -1);
450 if (ArgSetWalker
[0] == L
'-') {
451 Info
->Step
= 0 - (INTN
)ShellStrToUintn(ArgSetWalker
+1);
453 Info
->Step
= (INTN
)ShellStrToUintn(ArgSetWalker
);
462 if (ShellStatus
== SHELL_SUCCESS
) {
463 if (InternalIsAliasOnList(Info
->ReplacementName
, &CurrentScriptFile
->SubstList
)) {
464 Info
->RemoveSubstAlias
= FALSE
;
466 Info
->RemoveSubstAlias
= TRUE
;
469 CurrentScriptFile
->CurrentCommand
->Data
= Info
;
471 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT
), gShellLevel1HiiHandle
, ArgSet
, ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
472 ShellStatus
= SHELL_INVALID_PARAMETER
;
476 // These need to be NULL since they are used to determine if this is the first pass later on...
478 ASSERT(ArgSetWalker
== NULL
);
479 ASSERT(ArgSet
== NULL
);
482 Info
= (SHELL_FOR_INFO
*)CurrentScriptFile
->CurrentCommand
->Data
;
483 if (CurrentScriptFile
->CurrentCommand
->Reset
) {
484 Info
->CurrentValue
= (CHAR16
*)Info
->Set
;
486 CurrentScriptFile
->CurrentCommand
->Reset
= FALSE
;
488 if (ShellStatus
== SHELL_SUCCESS
) {
489 ASSERT(Info
!= NULL
);
490 if (Info
->Step
!= 0) {
492 // only advance if not the first pass
496 // sequence version of for loop...
498 Info
->Current
+= Info
->Step
;
501 TempString
= AllocateZeroPool(50*sizeof(CHAR16
));
502 UnicodeSPrint(TempString
, 50*sizeof(CHAR16
), L
"%d", Info
->Current
);
503 InternalUpdateAliasOnList(Info
->ReplacementName
, TempString
, &CurrentScriptFile
->SubstList
);
504 FreePool(TempString
);
506 if ((Info
->Step
> 0 && Info
->Current
> Info
->End
) || (Info
->Step
< 0 && Info
->Current
< Info
->End
)) {
507 CurrentScriptFile
->CurrentCommand
->Data
= NULL
;
509 // find the matching endfor (we're done with the loop)
511 if (!MoveToTag(GetNextNode
, L
"endfor", L
"for", NULL
, CurrentScriptFile
, TRUE
, FALSE
, FALSE
)) {
512 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SYNTAX_NO_MATCHING
), gShellLevel1HiiHandle
, L
"EndFor", L
"For", ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
513 ShellStatus
= SHELL_DEVICE_ERROR
;
515 if (Info
->RemoveSubstAlias
) {
517 // remove item from list
519 InternalRemoveAliasFromList(Info
->ReplacementName
, &CurrentScriptFile
->SubstList
);
525 // Must be in 'in' version of for loop...
527 ASSERT(Info
->Set
!= NULL
);
528 if (Info
->CurrentValue
!= NULL
&& *Info
->CurrentValue
!= CHAR_NULL
) {
529 if (Info
->CurrentValue
[0] == L
'\"') {
530 Info
->CurrentValue
++;
532 while (Info
->CurrentValue
[0] == L
' ') {
533 Info
->CurrentValue
++;
535 if (Info
->CurrentValue
[0] == L
'\"') {
536 Info
->CurrentValue
++;
539 // do the next one of the set
541 ASSERT(TempString
== NULL
);
542 TempString
= StrnCatGrow(&TempString
, NULL
, Info
->CurrentValue
, 0);
543 TempSpot
= StrStr(TempString
, L
"\" \"");
544 if (TempSpot
!= NULL
) {
545 *TempSpot
= CHAR_NULL
;
547 while (TempString
[StrLen(TempString
)-1] == L
'\"') {
548 TempString
[StrLen(TempString
)-1] = CHAR_NULL
;
550 InternalUpdateAliasOnList(Info
->ReplacementName
, TempString
, &CurrentScriptFile
->SubstList
);
551 Info
->CurrentValue
+= StrLen(TempString
);
553 if (Info
->CurrentValue
[0] == L
'\"') {
554 Info
->CurrentValue
++;
556 while (Info
->CurrentValue
[0] == L
' ') {
557 Info
->CurrentValue
++;
559 if (Info
->CurrentValue
[0] == L
'\"') {
560 Info
->CurrentValue
++;
562 FreePool(TempString
);
565 CurrentScriptFile
->CurrentCommand
->Data
= NULL
;
567 // find the matching endfor (we're done with the loop)
569 if (!MoveToTag(GetNextNode
, L
"endfor", L
"for", NULL
, CurrentScriptFile
, TRUE
, FALSE
, FALSE
)) {
570 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SYNTAX_NO_MATCHING
), gShellLevel1HiiHandle
, L
"EndFor", L
"For", ShellCommandGetCurrentScriptFile()->CurrentCommand
->Line
);
571 ShellStatus
= SHELL_DEVICE_ERROR
;
573 if (Info
->RemoveSubstAlias
) {
575 // remove item from list
577 InternalRemoveAliasFromList(Info
->ReplacementName
, &CurrentScriptFile
->SubstList
);
583 if (ArgSet
!= NULL
) {
586 return (ShellStatus
);