1/** @file
2  Main file for Parse shell level 2 function.
3
4  (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5  Copyright (c) 2009 - 2012, 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 "UefiShellLevel2CommandsLib.h"
17
18/**
19  Check if data is coming from StdIn output.
20
21  @param[in] None
22
23  @retval TRUE  StdIn stream data available to parse
24  @retval FALSE StdIn stream data is not available to parse.
25**/
26BOOLEAN
27IsStdInDataAvailable (
28  VOID
29  )
30{
31  SHELL_FILE_HANDLE FileHandle;
32  EFI_STATUS        Status;
33  CHAR16            CharBuffer;
34  UINTN             CharSize;
35  UINT64            OriginalFilePosition;
36
37  Status               = EFI_SUCCESS;
38  FileHandle           = NULL;
39  OriginalFilePosition = 0;
40
41  if (ShellOpenFileByName (L">i", &FileHandle, EFI_FILE_MODE_READ, 0) == EFI_SUCCESS) {
42    CharSize = sizeof(CHAR16);
43    gEfiShellProtocol->GetFilePosition (FileHandle, &OriginalFilePosition);
44    Status = gEfiShellProtocol->ReadFile (FileHandle, &CharSize, &CharBuffer);
45    if (EFI_ERROR (Status) || (CharSize != sizeof(CHAR16))) {
46      return FALSE;
47    }
48    gEfiShellProtocol->SetFilePosition(FileHandle, OriginalFilePosition);
49  }
50
51  if (FileHandle == NULL) {
52    return FALSE;
53  } else {
54    return TRUE;
55  }
56}
57
58/**
59  Function to read a single line (up to but not including the \n) using StdIn data from a SHELL_FILE_HANDLE.
60
61  If the position upon start is 0, then the Ascii Boolean will be set.  This should be
62  maintained and not changed for all operations with the same file.
63
64  @param[in]       Handle        SHELL_FILE_HANDLE to read from.
65  @param[in, out]  Buffer        The pointer to buffer to read into.
66  @param[in, out]  Size          The pointer to number of bytes in Buffer.
67  @param[in]       Truncate      If the buffer is large enough, this has no effect.
68                                 If the buffer is is too small and Truncate is TRUE,
69                                 the line will be truncated.
70                                 If the buffer is is too small and Truncate is FALSE,
71                                 then no read will occur.
72
73  @retval EFI_SUCCESS           The operation was successful.  The line is stored in
74                                Buffer.
75  @retval EFI_INVALID_PARAMETER Handle was NULL.
76  @retval EFI_INVALID_PARAMETER Size was NULL.
77  @retval EFI_BUFFER_TOO_SMALL  Size was not large enough to store the line.
78                                Size was updated to the minimum space required.
79**/
80EFI_STATUS
81EFIAPI
82ShellFileHandleReadStdInLine(
83  IN SHELL_FILE_HANDLE          Handle,
84  IN OUT CHAR16                 *Buffer,
85  IN OUT UINTN                  *Size,
86  IN BOOLEAN                    Truncate
87  )
88{
89  EFI_STATUS  Status;
90  CHAR16      CharBuffer;
91  UINTN       CharSize;
92  UINTN       CountSoFar;
93  UINT64      OriginalFilePosition;
94
95
96  if (Handle == NULL
97    ||Size   == NULL
98   ){
99    return (EFI_INVALID_PARAMETER);
100  }
101  if (Buffer == NULL) {
102    ASSERT(*Size == 0);
103  } else {
104    *Buffer = CHAR_NULL;
105  }
106  gEfiShellProtocol->GetFilePosition (Handle, &OriginalFilePosition);
107
108  for (CountSoFar = 0;;CountSoFar++){
109    CharBuffer = 0;
110    CharSize = sizeof(CHAR16);
111    Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer);
112    if (  EFI_ERROR(Status)
113       || CharSize == 0
114       || (CharBuffer == L'\n')
115     ){
116      break;
117    }
118    //
119    // if we have space save it...
120    //
121    if ((CountSoFar+1)*sizeof(CHAR16) < *Size){
122      ASSERT(Buffer != NULL);
123      ((CHAR16*)Buffer)[CountSoFar] = CharBuffer;
124      ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL;
125    }
126  }
127
128  //
129  // if we ran out of space tell when...
130  //
131  if ((CountSoFar+1)*sizeof(CHAR16) > *Size){
132    *Size = (CountSoFar+1)*sizeof(CHAR16);
133    if (!Truncate) {
134      gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
135    } else {
136      DEBUG((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine"));
137    }
138    return (EFI_BUFFER_TOO_SMALL);
139  }
140  while(Buffer[StrLen(Buffer)-1] == L'\r') {
141    Buffer[StrLen(Buffer)-1] = CHAR_NULL;
142  }
143
144  return (Status);
145}
146
147
148/**
149  Function to read a single line using StdIn from a SHELL_FILE_HANDLE. The \n is not included in the returned
150  buffer.  The returned buffer must be callee freed.
151
152  If the position upon start is 0, then the Ascii Boolean will be set.  This should be
153  maintained and not changed for all operations with the same file.
154
155  @param[in]       Handle        SHELL_FILE_HANDLE to read from.
156
157  @return                        The line of text from the file.
158  @retval NULL                   There was not enough memory available.
159
160  @sa ShellFileHandleReadLine
161**/
162CHAR16*
163EFIAPI
164ParseReturnStdInLine (
165  IN SHELL_FILE_HANDLE Handle
166  )
167{
168  CHAR16          *RetVal;
169  UINTN           Size;
170  EFI_STATUS      Status;
171
172  Size   = 0;
173  RetVal = NULL;
174
175  Status = ShellFileHandleReadStdInLine (Handle, RetVal, &Size, FALSE);
176  if (Status == EFI_BUFFER_TOO_SMALL) {
177    RetVal = AllocateZeroPool(Size);
178    if (RetVal == NULL) {
179      return (NULL);
180    }
181    Status = ShellFileHandleReadStdInLine (Handle, RetVal, &Size, FALSE);
182
183  }
184  if (EFI_ERROR(Status) && (RetVal != NULL)) {
185    FreePool(RetVal);
186    RetVal = NULL;
187  }
188  return (RetVal);
189}
190
191/**
192  Handle stings for SFO Output with escape character ^ in a string
193  1. Quotation marks in the string must be escaped by using a ^ character (i.e. ^").
194  2. The ^ character may be inserted using ^^.
195
196  @param[in]  String  The Unicode NULL-terminated string.
197
198  @retval NewString   The new string handled for SFO.
199**/
200EFI_STRING
201HandleStringWithEscapeCharForParse (
202  IN      CHAR16  *String
203  )
204{
205  EFI_STRING  NewStr;
206  EFI_STRING  StrWalker;
207  EFI_STRING  ReturnStr;
208
209  if (String == NULL) {
210    return NULL;
211  }
212
213  //
214  // start to parse the input string.
215  //
216  NewStr = AllocateZeroPool (StrSize (String));
217  if (NewStr == NULL) {
218    return NULL;
219  }
220  ReturnStr = NewStr;
221  StrWalker = String;
222  while (*StrWalker != CHAR_NULL) {
223    if (*StrWalker == L'^' && (*(StrWalker + 1) == L'^' || *(StrWalker + 1) == L'"')) {
224      *NewStr = *(StrWalker + 1);
225      StrWalker++;
226    } else {
227      *NewStr = *StrWalker;
228    }
229    StrWalker++;
230    NewStr++;
231  }
232
233  return ReturnStr;
234}
235
236
237/**
238  Do the actual parsing of the file.  the file should be SFO output from a
239  shell command or a similar format.
240
241  @param[in] FileName               The filename to open.
242  @param[in] TableName              The name of the table to find.
243  @param[in] ColumnIndex            The column number to get.
244  @param[in] TableNameInstance      Which instance of the table to get (row).
245  @param[in] ShellCommandInstance   Which instance of the command to get.
246  @param[in] StreamingUnicode       Indicates Input file is StdIn Unicode streaming data or not
247
248  @retval SHELL_NOT_FOUND     The requested instance was not found.
249  @retval SHELL_SUCCESS       The operation was successful.
250**/
251SHELL_STATUS
252EFIAPI
253PerformParsing(
254  IN CONST CHAR16 *FileName,
255  IN CONST CHAR16 *TableName,
256  IN CONST UINTN  ColumnIndex,
257  IN CONST UINTN  TableNameInstance,
258  IN CONST UINTN  ShellCommandInstance,
259  IN BOOLEAN      StreamingUnicode
260  )
261{
262  SHELL_FILE_HANDLE FileHandle;
263  EFI_STATUS        Status;
264  BOOLEAN           Ascii;
265  UINTN             LoopVariable;
266  UINTN             ColumnLoop;
267  CHAR16            *TempLine;
268  CHAR16            *ColumnPointer;
269  SHELL_STATUS      ShellStatus;
270  CHAR16            *TempSpot;
271  CHAR16            *SfoString;
272
273  ASSERT(FileName   != NULL);
274  ASSERT(TableName  != NULL);
275
276  ShellStatus       = SHELL_SUCCESS;
277
278  Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ, 0);
279  if (EFI_ERROR(Status)) {
280    ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), gShellLevel2HiiHandle, L"parse", FileName);
281    ShellStatus = SHELL_NOT_FOUND;
282  } else if (!EFI_ERROR (FileHandleIsDirectory (FileHandle))) {
283    ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_NOT_FILE), gShellLevel2HiiHandle, L"parse", FileName);
284    ShellStatus = SHELL_NOT_FOUND;
285  } else {
286    for (LoopVariable = 0 ; LoopVariable < ShellCommandInstance && !ShellFileHandleEof(FileHandle);) {
287     if (StreamingUnicode) {
288       TempLine = ParseReturnStdInLine (FileHandle);
289     } else {
290       TempLine = ShellFileHandleReturnLine (FileHandle, &Ascii);
291     }
292
293      if ((TempLine == NULL) || (*TempLine == CHAR_NULL && StreamingUnicode)) {
294         break;
295      }
296
297      //
298      // Search for "ShellCommand," in the file to start the SFO table
299      // for a given ShellCommand.  The UEFI Shell spec does not specify
300      // a space after the comma.
301      //
302      if (StrStr (TempLine, L"ShellCommand,") == TempLine) {
303        LoopVariable++;
304      }
305      SHELL_FREE_NON_NULL(TempLine);
306    }
307    if (LoopVariable == ShellCommandInstance) {
308      LoopVariable = 0;
309      while(1) {
310        if (StreamingUnicode) {
311          TempLine = ParseReturnStdInLine (FileHandle);
312        } else {
313          TempLine = ShellFileHandleReturnLine (FileHandle, &Ascii);
314        }
315        if (TempLine == NULL
316            || *TempLine == CHAR_NULL
317            || StrStr (TempLine, L"ShellCommand,") == TempLine) {
318          SHELL_FREE_NON_NULL(TempLine);
319          break;
320        }
321        if (StrStr (TempLine, TableName) == TempLine) {
322          LoopVariable++;
323          if (LoopVariable == TableNameInstance
324              || (TableNameInstance == (UINTN)-1)) {
325            for (ColumnLoop = 1, ColumnPointer = TempLine; ColumnLoop < ColumnIndex && ColumnPointer != NULL && *ColumnPointer != CHAR_NULL; ColumnLoop++) {
326              ColumnPointer = StrStr (ColumnPointer, L",\"");
327              if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL){
328                ColumnPointer++;
329              }
330            }
331            if (ColumnLoop == ColumnIndex) {
332              if (ColumnPointer == NULL) {
333                ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_VALUE), gShellLevel2HiiHandle, L"parse", L"Column Index");
334                ShellStatus = SHELL_INVALID_PARAMETER;
335              } else {
336                TempSpot = StrStr (ColumnPointer, L",\"");
337                if (TempSpot != NULL) {
338                  *TempSpot = CHAR_NULL;
339                }
340                while (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[0] == L' '){
341                  ColumnPointer++;
342                }
343                if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[0] == L'\"'){
344                  ColumnPointer++;
345                }
346                if (ColumnPointer != NULL && *ColumnPointer != CHAR_NULL && ColumnPointer[StrLen (ColumnPointer) - 1] == L'\"'){
347                  ColumnPointer[StrLen (ColumnPointer) - 1] = CHAR_NULL;
348                }
349                SfoString = HandleStringWithEscapeCharForParse (ColumnPointer);
350                if (SfoString != NULL) {
351                  ShellPrintEx (-1, -1, L"%s\r\n", SfoString);
352                  SHELL_FREE_NON_NULL (SfoString);
353                }
354              }
355            }
356          }
357        }
358        SHELL_FREE_NON_NULL(TempLine);
359      }
360    }
361  }
362  return (ShellStatus);
363}
364
365STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
366  {L"-i", TypeValue},
367  {L"-s", TypeValue},
368  {NULL, TypeMax}
369  };
370
371/**
372  Function for 'parse' command.
373
374  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
375  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
376**/
377SHELL_STATUS
378EFIAPI
379ShellCommandRunParse (
380  IN EFI_HANDLE        ImageHandle,
381  IN EFI_SYSTEM_TABLE  *SystemTable
382  )
383{
384  EFI_STATUS          Status;
385  LIST_ENTRY          *Package;
386  CHAR16              *ProblemParam;
387  CONST CHAR16        *FileName;
388  CONST CHAR16        *TableName;
389  CONST CHAR16        *ColumnString;
390  SHELL_STATUS        ShellStatus;
391  UINTN               ShellCommandInstance;
392  UINTN               TableNameInstance;
393  BOOLEAN             StreamingUnicode;
394
395  ShellStatus      = SHELL_SUCCESS;
396  ProblemParam     = NULL;
397  StreamingUnicode = FALSE;
398
399  //
400  // initialize the shell lib (we must be in non-auto-init...)
401  //
402  Status = ShellInitialize();
403  ASSERT_EFI_ERROR(Status);
404
405  //
406  // parse the command line
407  //
408  Status = ShellCommandLineParseEx (ParamList, &Package, &ProblemParam, TRUE, FALSE);
409  if (EFI_ERROR(Status)) {
410    if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
411      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, L"parse", ProblemParam);
412      FreePool(ProblemParam);
413      ShellStatus = SHELL_INVALID_PARAMETER;
414    } else {
415      ASSERT(FALSE);
416    }
417  } else {
418    StreamingUnicode = IsStdInDataAvailable ();
419    if ((!StreamingUnicode && (ShellCommandLineGetCount(Package) < 4)) ||
420        (ShellCommandLineGetCount(Package) < 3)) {
421      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), gShellLevel2HiiHandle, L"parse");
422      ShellStatus = SHELL_INVALID_PARAMETER;
423    } else if ((StreamingUnicode && (ShellCommandLineGetCount(Package) > 3)) ||
424                (ShellCommandLineGetCount(Package) > 4)) {
425      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle, L"parse");
426      ShellStatus = SHELL_INVALID_PARAMETER;
427    } else {
428      if (StreamingUnicode) {
429        FileName         = L">i";
430        TableName        = ShellCommandLineGetRawValue(Package, 1);
431        ColumnString     = ShellCommandLineGetRawValue(Package, 2);
432      } else {
433        FileName         = ShellCommandLineGetRawValue(Package, 1);
434        TableName        = ShellCommandLineGetRawValue(Package, 2);
435        ColumnString     = ShellCommandLineGetRawValue(Package, 3);
436      }
437      if (ShellCommandLineGetValue(Package, L"-i") == NULL) {
438        TableNameInstance = (UINTN)-1;
439      } else {
440        TableNameInstance = ShellStrToUintn(ShellCommandLineGetValue(Package, L"-i"));
441      }
442      if (ShellCommandLineGetValue(Package, L"-s") == NULL) {
443        ShellCommandInstance = 1;
444      } else {
445        ShellCommandInstance = ShellStrToUintn(ShellCommandLineGetValue(Package, L"-s"));
446      }
447
448      ShellStatus = PerformParsing(FileName, TableName, ShellStrToUintn(ColumnString), TableNameInstance, ShellCommandInstance, StreamingUnicode);
449    }
450  }
451
452  //
453  // free the command line package
454  //
455  ShellCommandLineFreeVarList (Package);
456
457  return (ShellStatus);
458}
459
460