1/** @file
2  Main file for endfor and for shell level 1 functions.
3
4  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
5  Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
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 "UefiShellLevel1CommandsLib.h"
17#include <Library/PrintLib.h>
18
19/**
20  Determine if a valid string is a valid number for the 'for' command.
21
22  @param[in] Number The pointer to the string representation of the number to test.
23
24  @retval TRUE    The number is valid.
25  @retval FALSE   The number is not valid.
26**/
27BOOLEAN
28ShellIsValidForNumber (
29  IN CONST CHAR16 *Number
30  )
31{
32  if (Number == NULL || *Number == CHAR_NULL) {
33    return (FALSE);
34  }
35
36  if (*Number == L'-') {
37    Number++;
38  }
39
40  if (StrLen(Number) == 0) {
41    return (FALSE);
42  }
43
44  if (StrLen(Number) >= 7) {
45    if ((StrStr(Number, L" ") == NULL) || (((StrStr(Number, L" ") != NULL) && (StrStr(Number, L" ") - Number) >= 7))) {
46      return (FALSE);
47    }
48  }
49
50  if (!ShellIsDecimalDigitCharacter(*Number)) {
51    return (FALSE);
52  }
53
54  return (TRUE);
55}
56
57/**
58  Function for 'endfor' command.
59
60  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
61  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
62**/
63SHELL_STATUS
64EFIAPI
65ShellCommandRunEndFor (
66  IN EFI_HANDLE        ImageHandle,
67  IN EFI_SYSTEM_TABLE  *SystemTable
68  )
69{
70  EFI_STATUS          Status;
71  BOOLEAN             Found;
72  SCRIPT_FILE         *CurrentScriptFile;
73
74  Status = CommandInit();
75  ASSERT_EFI_ERROR(Status);
76
77  if (!gEfiShellProtocol->BatchIsActive()) {
78    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_NO_SCRIPT), gShellLevel1HiiHandle, L"endfor");
79    return (SHELL_UNSUPPORTED);
80  }
81
82  if (gEfiShellParametersProtocol->Argc > 1) {
83    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel1HiiHandle, L"endfor");
84    return (SHELL_INVALID_PARAMETER);
85  }
86
87  Found = MoveToTag(GetPreviousNode, L"for", L"endfor", NULL, ShellCommandGetCurrentScriptFile(), FALSE, FALSE, FALSE);
88
89  if (!Found) {
90    CurrentScriptFile = ShellCommandGetCurrentScriptFile();
91    ShellPrintHiiEx(
92      -1,
93      -1,
94      NULL,
95      STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
96      gShellLevel1HiiHandle,
97      L"For",
98      L"EndFor",
99      CurrentScriptFile!=NULL
100        && CurrentScriptFile->CurrentCommand!=NULL
101          ? CurrentScriptFile->CurrentCommand->Line:0);
102    return (SHELL_NOT_FOUND);
103  }
104  return (SHELL_SUCCESS);
105}
106
107typedef struct {
108  UINT32          Signature;
109  INTN            Current;
110  INTN            End;
111  INTN            Step;
112  CHAR16          *ReplacementName;
113  CHAR16          *CurrentValue;
114  BOOLEAN         RemoveSubstAlias;
115  CHAR16          Set[1];
116  } SHELL_FOR_INFO;
117#define SIZE_OF_SHELL_FOR_INFO OFFSET_OF (SHELL_FOR_INFO, Set)
118#define SHELL_FOR_INFO_SIGNATURE SIGNATURE_32 ('S', 'F', 'I', 's')
119
120/**
121  Update the value of a given alias on the list.  If the alias is not there then add it.
122
123  @param[in] Alias               The alias to test for.
124  @param[in] CommandString       The updated command string.
125  @param[in, out] List           The list to search.
126
127  @retval EFI_SUCCESS           The operation was completed successfully.
128  @retval EFI_OUT_OF_RESOURCES  There was not enough free memory.
129**/
130EFI_STATUS
131InternalUpdateAliasOnList(
132  IN CONST CHAR16       *Alias,
133  IN CONST CHAR16       *CommandString,
134  IN OUT LIST_ENTRY     *List
135  )
136{
137  ALIAS_LIST *Node;
138  BOOLEAN    Found;
139
140  //
141  // assert for NULL parameter
142  //
143  ASSERT(Alias != NULL);
144
145  //
146  // check for the Alias
147  //
148  for ( Node = (ALIAS_LIST *)GetFirstNode(List), Found = FALSE
149      ; !IsNull(List, &Node->Link)
150      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
151     ){
152    ASSERT(Node->CommandString != NULL);
153    ASSERT(Node->Alias != NULL);
154    if (StrCmp(Node->Alias, Alias)==0) {
155      FreePool(Node->CommandString);
156      Node->CommandString = NULL;
157      Node->CommandString = StrnCatGrow(&Node->CommandString, NULL, CommandString, 0);
158      Found = TRUE;
159      break;
160    }
161  }
162  if (!Found) {
163    Node = AllocateZeroPool(sizeof(ALIAS_LIST));
164    if (Node == NULL) {
165      return (EFI_OUT_OF_RESOURCES);
166    }
167    ASSERT(Node->Alias == NULL);
168    Node->Alias         = StrnCatGrow(&Node->Alias, NULL, Alias, 0);
169    ASSERT(Node->CommandString == NULL);
170    Node->CommandString = StrnCatGrow(&Node->CommandString, NULL, CommandString, 0);
171    InsertTailList(List, &Node->Link);
172  }
173  return (EFI_SUCCESS);
174}
175
176/**
177  Find out if an alias is on the given list.
178
179  @param[in] Alias              The alias to test for.
180  @param[in] List               The list to search.
181
182  @retval TRUE                  The alias is on the list.
183  @retval FALSE                 The alias is not on the list.
184**/
185BOOLEAN
186InternalIsAliasOnList(
187  IN CONST CHAR16       *Alias,
188  IN CONST LIST_ENTRY   *List
189  )
190{
191  ALIAS_LIST *Node;
192
193  //
194  // assert for NULL parameter
195  //
196  ASSERT(Alias != NULL);
197
198  //
199  // check for the Alias
200  //
201  for ( Node = (ALIAS_LIST *)GetFirstNode(List)
202      ; !IsNull(List, &Node->Link)
203      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
204     ){
205    ASSERT(Node->CommandString != NULL);
206    ASSERT(Node->Alias != NULL);
207    if (StrCmp(Node->Alias, Alias)==0) {
208      return (TRUE);
209    }
210  }
211  return (FALSE);
212}
213
214/**
215  Remove an alias from the given list.
216
217  @param[in] Alias               The alias to remove.
218  @param[in, out] List           The list to search.
219**/
220BOOLEAN
221InternalRemoveAliasFromList(
222  IN CONST CHAR16       *Alias,
223  IN OUT LIST_ENTRY     *List
224  )
225{
226  ALIAS_LIST *Node;
227
228  //
229  // assert for NULL parameter
230  //
231  ASSERT(Alias != NULL);
232
233  //
234  // check for the Alias
235  //
236  for ( Node = (ALIAS_LIST *)GetFirstNode(List)
237      ; !IsNull(List, &Node->Link)
238      ; Node = (ALIAS_LIST *)GetNextNode(List, &Node->Link)
239     ){
240    ASSERT(Node->CommandString != NULL);
241    ASSERT(Node->Alias != NULL);
242    if (StrCmp(Node->Alias, Alias)==0) {
243      RemoveEntryList(&Node->Link);
244      FreePool(Node->Alias);
245      FreePool(Node->CommandString);
246      FreePool(Node);
247      return (TRUE);
248    }
249  }
250  return (FALSE);
251}
252
253/**
254  Function to determine whether a string is decimal or hex representation of a number
255  and return the number converted from the string.
256
257  @param[in] String   String representation of a number
258
259  @return             the number
260  @retval (UINTN)(-1) An error ocurred.
261**/
262UINTN
263ReturnUintn(
264  IN CONST CHAR16 *String
265  )
266{
267  UINT64        RetVal;
268
269  if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, FALSE, TRUE))) {
270    return ((UINTN)RetVal);
271  }
272  return ((UINTN)(-1));
273}
274
275/**
276  Function for 'for' command.
277
278  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
279  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
280**/
281SHELL_STATUS
282EFIAPI
283ShellCommandRunFor (
284  IN EFI_HANDLE        ImageHandle,
285  IN EFI_SYSTEM_TABLE  *SystemTable
286  )
287{
288  EFI_STATUS          Status;
289  SHELL_STATUS        ShellStatus;
290  SCRIPT_FILE         *CurrentScriptFile;
291  CHAR16              *ArgSet;
292  CHAR16              *ArgSetWalker;
293  CHAR16              *Parameter;
294  UINTN               ArgSize;
295  UINTN               LoopVar;
296  SHELL_FOR_INFO      *Info;
297  CHAR16              *TempString;
298  CHAR16              *TempSpot;
299  BOOLEAN             FirstPass;
300  EFI_SHELL_FILE_INFO *Node;
301  EFI_SHELL_FILE_INFO *FileList;
302  UINTN               NewSize;
303
304  ArgSet              = NULL;
305  ArgSize             = 0;
306  ShellStatus         = SHELL_SUCCESS;
307  ArgSetWalker        = NULL;
308  TempString          = NULL;
309  Parameter           = NULL;
310  FirstPass           = FALSE;
311
312  //
313  // initialize the shell lib (we must be in non-auto-init...)
314  //
315  Status = ShellInitialize();
316  ASSERT_EFI_ERROR(Status);
317
318  Status = CommandInit();
319  ASSERT_EFI_ERROR(Status);
320
321  if (!gEfiShellProtocol->BatchIsActive()) {
322    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_NO_SCRIPT), gShellLevel1HiiHandle, L"for");
323    return (SHELL_UNSUPPORTED);
324  }
325
326  if (gEfiShellParametersProtocol->Argc < 4) {
327    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel1HiiHandle, L"for");
328    return (SHELL_INVALID_PARAMETER);
329  }
330
331  CurrentScriptFile = ShellCommandGetCurrentScriptFile();
332  ASSERT(CurrentScriptFile != NULL);
333
334  if ((CurrentScriptFile->CurrentCommand != NULL) && (CurrentScriptFile->CurrentCommand->Data == NULL)) {
335    FirstPass = TRUE;
336
337    //
338    // Make sure that an End exists.
339    //
340    if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, TRUE, FALSE)) {
341      ShellPrintHiiEx(
342        -1,
343        -1,
344        NULL,
345        STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
346        gShellLevel1HiiHandle,
347        L"EndFor",
348        L"For",
349        CurrentScriptFile->CurrentCommand->Line);
350      return (SHELL_DEVICE_ERROR);
351    }
352
353    //
354    // Process the line.
355    //
356    if (gEfiShellParametersProtocol->Argv[1][0] != L'%' || gEfiShellParametersProtocol->Argv[1][2] != CHAR_NULL
357      ||!((gEfiShellParametersProtocol->Argv[1][1] >= L'a' && gEfiShellParametersProtocol->Argv[1][1] <= L'z')
358       ||(gEfiShellParametersProtocol->Argv[1][1] >= L'A' && gEfiShellParametersProtocol->Argv[1][1] <= L'Z'))
359     ) {
360      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_INV_VAR), gShellLevel1HiiHandle, gEfiShellParametersProtocol->Argv[1]);
361      return (SHELL_INVALID_PARAMETER);
362    }
363
364    if (gUnicodeCollation->StriColl(
365        gUnicodeCollation,
366        L"in",
367        gEfiShellParametersProtocol->Argv[2]) == 0) {
368      for (LoopVar = 0x3 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
369        ASSERT((ArgSet == NULL && ArgSize == 0) || (ArgSet != NULL));
370        if (StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"*") != NULL
371          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"?") != NULL
372          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"[") != NULL
373          ||StrStr(gEfiShellParametersProtocol->Argv[LoopVar], L"]") != NULL) {
374          FileList = NULL;
375          Status = ShellOpenFileMetaArg ((CHAR16*)gEfiShellParametersProtocol->Argv[LoopVar], EFI_FILE_MODE_READ, &FileList);
376          if (EFI_ERROR(Status) || FileList == NULL || IsListEmpty(&FileList->Link)) {
377            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
378            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, gEfiShellParametersProtocol->Argv[LoopVar], 0);
379            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
380          } else {
381            for (Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&FileList->Link)
382              ;  !IsNull(&FileList->Link, &Node->Link)
383              ;  Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&FileList->Link, &Node->Link)
384             ){
385              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
386              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Node->FullName, 0);
387              ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
388            }
389            ShellCloseFileMetaArg(&FileList);
390          }
391        } else {
392          Parameter = gEfiShellParametersProtocol->Argv[LoopVar];
393          if (Parameter[0] == L'\"' && Parameter[StrLen(Parameter)-1] == L'\"') {
394            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
395            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Parameter, 0);
396          } else {
397            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" \"", 0);
398            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, Parameter, 0);
399            ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
400          }
401        }
402      }
403      if (ArgSet == NULL) {
404        ShellStatus = SHELL_OUT_OF_RESOURCES;
405      } else {
406        //
407        // set up for an 'in' for loop
408        //
409        NewSize = StrSize(ArgSet);
410        NewSize += sizeof(SHELL_FOR_INFO)+StrSize(gEfiShellParametersProtocol->Argv[1]);
411        Info = AllocateZeroPool(NewSize);
412        if (Info == NULL) {
413          FreePool (ArgSet);
414          return SHELL_OUT_OF_RESOURCES;
415        }
416        Info->Signature = SHELL_FOR_INFO_SIGNATURE;
417        CopyMem(Info->Set, ArgSet, StrSize(ArgSet));
418        NewSize = StrSize(gEfiShellParametersProtocol->Argv[1]);
419        CopyMem(Info->Set+(StrSize(ArgSet)/sizeof(Info->Set[0])), gEfiShellParametersProtocol->Argv[1], NewSize);
420        Info->ReplacementName = Info->Set+StrSize(ArgSet)/sizeof(Info->Set[0]);
421        Info->CurrentValue  = (CHAR16*)Info->Set;
422        Info->Step          = 0;
423        Info->Current       = 0;
424        Info->End           = 0;
425
426        if (InternalIsAliasOnList(Info->ReplacementName, &CurrentScriptFile->SubstList)) {
427          Info->RemoveSubstAlias  = FALSE;
428        } else {
429          Info->RemoveSubstAlias  = TRUE;
430        }
431        CurrentScriptFile->CurrentCommand->Data = Info;
432      }
433    } else if (gUnicodeCollation->StriColl(
434        gUnicodeCollation,
435        L"run",
436        gEfiShellParametersProtocol->Argv[2]) == 0) {
437      for (LoopVar = 0x3 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
438        ASSERT((ArgSet == NULL && ArgSize == 0) || (ArgSet != NULL));
439        if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L")") != NULL &&
440            (LoopVar + 1) < gEfiShellParametersProtocol->Argc
441           ) {
442          return (SHELL_INVALID_PARAMETER);
443        }
444        if (ArgSet == NULL) {
445//        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L"\"", 0);
446        } else {
447          ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
448        }
449        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, gEfiShellParametersProtocol->Argv[LoopVar], 0);
450//        ArgSet = StrnCatGrow(&ArgSet, &ArgSize, L" ", 0);
451      }
452      if (ArgSet == NULL) {
453        ShellStatus = SHELL_OUT_OF_RESOURCES;
454      } else {
455        //
456        // set up for a 'run' for loop
457        //
458        Info = AllocateZeroPool(sizeof(SHELL_FOR_INFO)+StrSize(gEfiShellParametersProtocol->Argv[1]));
459        if (Info == NULL) {
460          FreePool (ArgSet);
461          return SHELL_OUT_OF_RESOURCES;
462        }
463        Info->Signature = SHELL_FOR_INFO_SIGNATURE;
464        CopyMem(Info->Set, gEfiShellParametersProtocol->Argv[1], StrSize(gEfiShellParametersProtocol->Argv[1]));
465        Info->ReplacementName = Info->Set;
466        Info->CurrentValue    = NULL;
467        ArgSetWalker            = ArgSet;
468        if (ArgSetWalker[0] != L'(') {
469          ShellPrintHiiEx(
470            -1,
471            -1,
472            NULL,
473            STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
474            gShellLevel1HiiHandle,
475            ArgSet,
476            CurrentScriptFile->CurrentCommand->Line);
477          ShellStatus = SHELL_INVALID_PARAMETER;
478        } else {
479          TempSpot = StrStr(ArgSetWalker, L")");
480          if (TempSpot != NULL) {
481            TempString = TempSpot+1;
482            if (*(TempString) != CHAR_NULL) {
483              while(TempString != NULL && *TempString == L' ') {
484                TempString++;
485              }
486              if (StrLen(TempString) > 0) {
487                TempSpot = NULL;
488              }
489            }
490          }
491          if (TempSpot == NULL) {
492            ShellPrintHiiEx(
493              -1,
494              -1,
495              NULL,
496              STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
497              gShellLevel1HiiHandle,
498              CurrentScriptFile->CurrentCommand->Line);
499            ShellStatus = SHELL_INVALID_PARAMETER;
500          } else {
501            *TempSpot = CHAR_NULL;
502            ArgSetWalker++;
503            while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
504              ArgSetWalker++;
505            }
506            if (!ShellIsValidForNumber(ArgSetWalker)) {
507              ShellPrintHiiEx(
508                -1,
509                -1,
510                NULL,
511                STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
512                gShellLevel1HiiHandle,
513                ArgSet,
514                CurrentScriptFile->CurrentCommand->Line);
515              ShellStatus = SHELL_INVALID_PARAMETER;
516            } else {
517              if (ArgSetWalker[0] == L'-') {
518                Info->Current = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
519              } else {
520                Info->Current = (INTN)ReturnUintn(ArgSetWalker);
521              }
522              ArgSetWalker  = StrStr(ArgSetWalker, L" ");
523              while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
524                ArgSetWalker++;
525              }
526              if (ArgSetWalker == NULL || *ArgSetWalker == CHAR_NULL || !ShellIsValidForNumber(ArgSetWalker)){
527                ShellPrintHiiEx(
528                  -1,
529                  -1,
530                  NULL,
531                  STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
532                  gShellLevel1HiiHandle,
533                  ArgSet,
534                  CurrentScriptFile->CurrentCommand->Line);
535                ShellStatus = SHELL_INVALID_PARAMETER;
536              } else {
537                if (ArgSetWalker[0] == L'-') {
538                  Info->End = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
539                } else {
540                  Info->End = (INTN)ReturnUintn(ArgSetWalker);
541                }
542                if (Info->Current < Info->End) {
543                  Info->Step            = 1;
544                } else {
545                  Info->Step            = -1;
546                }
547
548                ArgSetWalker  = StrStr(ArgSetWalker, L" ");
549                while (ArgSetWalker != NULL && ArgSetWalker[0] == L' ') {
550                  ArgSetWalker++;
551                }
552                if (ArgSetWalker != NULL && *ArgSetWalker != CHAR_NULL) {
553                  if (ArgSetWalker == NULL || *ArgSetWalker == CHAR_NULL || !ShellIsValidForNumber(ArgSetWalker)){
554                    ShellPrintHiiEx(
555                      -1,
556                      -1,
557                      NULL,
558                      STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
559                      gShellLevel1HiiHandle,
560                      ArgSet,
561                      CurrentScriptFile->CurrentCommand->Line);
562                    ShellStatus = SHELL_INVALID_PARAMETER;
563                  } else {
564                    if (*ArgSetWalker == L')') {
565                      ASSERT(Info->Step == 1 || Info->Step == -1);
566                    } else {
567                      if (ArgSetWalker[0] == L'-') {
568                        Info->Step = 0 - (INTN)ReturnUintn(ArgSetWalker+1);
569                      } else {
570                        Info->Step = (INTN)ReturnUintn(ArgSetWalker);
571                      }
572
573                      if (StrStr(ArgSetWalker, L" ") != NULL) {
574                        ShellPrintHiiEx(
575                          -1,
576                          -1,
577                          NULL,
578                          STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
579                          gShellLevel1HiiHandle,
580                          ArgSet,
581                          CurrentScriptFile->CurrentCommand->Line);
582                        ShellStatus = SHELL_INVALID_PARAMETER;
583                      }
584                    }
585                  }
586
587                }
588              }
589            }
590          }
591        }
592        if (ShellStatus == SHELL_SUCCESS) {
593          if (InternalIsAliasOnList(Info->ReplacementName, &CurrentScriptFile->SubstList)) {
594            Info->RemoveSubstAlias  = FALSE;
595          } else {
596            Info->RemoveSubstAlias  = TRUE;
597          }
598        }
599        if (CurrentScriptFile->CurrentCommand != NULL) {
600          CurrentScriptFile->CurrentCommand->Data = Info;
601        }
602      }
603    } else {
604      ShellPrintHiiEx(
605        -1,
606        -1,
607        NULL,
608        STRING_TOKEN (STR_GEN_PROBLEM_SCRIPT),
609        gShellLevel1HiiHandle,
610        ArgSet,
611        CurrentScriptFile!=NULL
612          && CurrentScriptFile->CurrentCommand!=NULL
613          ? CurrentScriptFile->CurrentCommand->Line:0);
614      ShellStatus = SHELL_INVALID_PARAMETER;
615    }
616  } else {
617    //
618    // These need to be NULL since they are used to determine if this is the first pass later on...
619    //
620    ASSERT(ArgSetWalker == NULL);
621    ASSERT(ArgSet       == NULL);
622  }
623
624  if (CurrentScriptFile != NULL && CurrentScriptFile->CurrentCommand != NULL) {
625    Info = (SHELL_FOR_INFO*)CurrentScriptFile->CurrentCommand->Data;
626    if (CurrentScriptFile->CurrentCommand->Reset) {
627      Info->CurrentValue  = (CHAR16*)Info->Set;
628      FirstPass = TRUE;
629      CurrentScriptFile->CurrentCommand->Reset = FALSE;
630    }
631  } else {
632    ShellStatus = SHELL_UNSUPPORTED;
633    Info = NULL;
634  }
635  if (ShellStatus == SHELL_SUCCESS) {
636    ASSERT(Info != NULL);
637    if (Info->Step != 0) {
638      //
639      // only advance if not the first pass
640      //
641      if (!FirstPass) {
642        //
643        // sequence version of for loop...
644        //
645        Info->Current += Info->Step;
646      }
647
648      TempString = AllocateZeroPool(50*sizeof(CHAR16));
649      UnicodeSPrint(TempString, 50*sizeof(CHAR16), L"%d", Info->Current);
650      InternalUpdateAliasOnList(Info->ReplacementName, TempString, &CurrentScriptFile->SubstList);
651      FreePool(TempString);
652
653      if ((Info->Step > 0 && Info->Current > Info->End) || (Info->Step < 0 && Info->Current < Info->End)) {
654        CurrentScriptFile->CurrentCommand->Data = NULL;
655        //
656        // find the matching endfor (we're done with the loop)
657        //
658        if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, FALSE, FALSE)) {
659          ShellPrintHiiEx(
660            -1,
661            -1,
662            NULL,
663            STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
664            gShellLevel1HiiHandle,
665            L"EndFor",
666            L"For",
667            CurrentScriptFile!=NULL
668              && CurrentScriptFile->CurrentCommand!=NULL
669              ? CurrentScriptFile->CurrentCommand->Line:0);
670          ShellStatus = SHELL_DEVICE_ERROR;
671        }
672        if (Info->RemoveSubstAlias) {
673          //
674          // remove item from list
675          //
676          InternalRemoveAliasFromList(Info->ReplacementName, &CurrentScriptFile->SubstList);
677        }
678        FreePool(Info);
679      }
680    } else {
681      //
682      // Must be in 'in' version of for loop...
683      //
684      ASSERT(Info->Set != NULL);
685      if (Info->CurrentValue != NULL && *Info->CurrentValue != CHAR_NULL) {
686        if (Info->CurrentValue[0] == L' ') {
687          Info->CurrentValue++;
688        }
689        if (Info->CurrentValue[0] == L'\"') {
690          Info->CurrentValue++;
691        }
692        //
693        // do the next one of the set
694        //
695        ASSERT(TempString == NULL);
696        TempString = StrnCatGrow(&TempString, NULL, Info->CurrentValue, 0);
697        if (TempString == NULL) {
698          ShellStatus = SHELL_OUT_OF_RESOURCES;
699        } else {
700          TempSpot   = StrStr(TempString, L"\" \"");
701          if (TempSpot != NULL) {
702            *TempSpot = CHAR_NULL;
703          }
704          while (TempString[StrLen(TempString)-1] == L'\"') {
705            TempString[StrLen(TempString)-1] = CHAR_NULL;
706          }
707          InternalUpdateAliasOnList(Info->ReplacementName, TempString, &CurrentScriptFile->SubstList);
708          Info->CurrentValue += StrLen(TempString);
709
710          if (Info->CurrentValue[0] == L'\"') {
711            Info->CurrentValue++;
712          }
713          FreePool(TempString);
714        }
715      } else {
716        CurrentScriptFile->CurrentCommand->Data = NULL;
717        //
718        // find the matching endfor (we're done with the loop)
719        //
720        if (!MoveToTag(GetNextNode, L"endfor", L"for", NULL, CurrentScriptFile, TRUE, FALSE, FALSE)) {
721          ShellPrintHiiEx(
722            -1,
723            -1,
724            NULL,
725            STRING_TOKEN (STR_SYNTAX_NO_MATCHING),
726            gShellLevel1HiiHandle,
727            L"EndFor",
728            L"For",
729            CurrentScriptFile!=NULL
730              && CurrentScriptFile->CurrentCommand!=NULL
731              ? CurrentScriptFile->CurrentCommand->Line:0);
732          ShellStatus = SHELL_DEVICE_ERROR;
733        }
734        if (Info->RemoveSubstAlias) {
735          //
736          // remove item from list
737          //
738          InternalRemoveAliasFromList(Info->ReplacementName, &CurrentScriptFile->SubstList);
739        }
740        FreePool(Info);
741      }
742    }
743  }
744  if (ArgSet != NULL) {
745    FreePool(ArgSet);
746  }
747  return (ShellStatus);
748}
749
750