Ls.c revision a405b86d274d32b92f69842bfb9a1ab143128f57
1/** @file
2  Main file for ls shell level 2 function.
3
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
9
10  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "UefiShellLevel2CommandsLib.h"
16#include <Guid/FileSystemInfo.h>
17
18/**
19  print out the list of files and directories from the LS command
20
21  @param[in] Rec            TRUE to automatically recurse into each found directory
22                            FALSE to only list the specified directory.
23  @param[in] Attribs        List of required Attribute for display.
24                            If 0 then all non-system and non-hidden files will be printed.
25  @param[in] Sfo            TRUE to use Standard Format Output, FALSE otherwise
26  @param[in] Path           String with starting path.
27  @param[in] First          TRUE for the original and FALSE for any recursion spawned instances.
28  @param[in] Count          The count of bits enabled in Attribs.
29  @param[in] TimeZone       The current time zone offset.
30
31  @retval SHELL_SUCCESS     the printing was sucessful.
32**/
33SHELL_STATUS
34EFIAPI
35PrintLsOutput(
36  IN CONST BOOLEAN Rec,
37  IN CONST UINT64  Attribs,
38  IN CONST BOOLEAN Sfo,
39  IN CONST CHAR16  *Path,
40  IN CONST BOOLEAN First,
41  IN CONST UINTN   Count,
42  IN CONST INT16   TimeZone
43  )
44{
45  EFI_STATUS            Status;
46  EFI_SHELL_FILE_INFO   *ListHead;
47  EFI_SHELL_FILE_INFO   *Node;
48  SHELL_STATUS          ShellStatus;
49  UINT64                FileCount;
50  UINT64                DirCount;
51  UINT64                FileSize;
52  CHAR16                *DirectoryName;
53  UINTN                 LongestPath;
54  EFI_FILE_SYSTEM_INFO  *SysInfo;
55  UINTN                 SysInfoSize;
56  SHELL_FILE_HANDLE     ShellFileHandle;
57  CHAR16                *CorrectedPath;
58  EFI_FILE_PROTOCOL     *EfiFpHandle;
59
60  FileCount     = 0;
61  DirCount      = 0;
62  FileSize      = 0;
63  ListHead      = NULL;
64  ShellStatus   = SHELL_SUCCESS;
65  LongestPath   = 0;
66  CorrectedPath = NULL;
67
68  CorrectedPath = StrnCatGrow(&CorrectedPath, NULL, Path, 0);
69  ASSERT(CorrectedPath != NULL);
70  ShellCommandCleanPath(CorrectedPath);
71
72  Status = ShellOpenFileMetaArg((CHAR16*)CorrectedPath, EFI_FILE_MODE_READ, &ListHead);
73  if (EFI_ERROR(Status)) {
74    return (SHELL_DEVICE_ERROR);
75  }
76  if (ListHead == NULL || IsListEmpty(&ListHead->Link)) {
77    //
78    // On the first one only we expect to find something...
79    // do we find the . and .. directories otherwise?
80    //
81    if (First) {
82      return (SHELL_NOT_FOUND);
83    }
84    return (SHELL_SUCCESS);
85  }
86
87  if (Sfo && First) {
88    //
89    // Get the first valid handle (directories)
90    //
91    for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
92        ; !IsNull(&ListHead->Link, &Node->Link) && Node->Handle == NULL
93        ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
94       );
95
96    if (Node->Handle == NULL) {
97      DirectoryName = GetFullyQualifiedPath(((EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link))->FullName);
98
99      //
100      // We need to open something up to get system information
101      //
102      Status = gEfiShellProtocol->OpenFileByName(
103        DirectoryName,
104        &ShellFileHandle,
105        EFI_FILE_MODE_READ);
106
107      ASSERT_EFI_ERROR(Status);
108      FreePool(DirectoryName);
109
110      //
111      // Get the Volume Info from ShellFileHandle
112      //
113      SysInfo     = NULL;
114      SysInfoSize = 0;
115      EfiFpHandle = ConvertShellHandleToEfiFileProtocol(ShellFileHandle);
116      Status = EfiFpHandle->GetInfo(
117        EfiFpHandle,
118        &gEfiFileSystemInfoGuid,
119        &SysInfoSize,
120        SysInfo);
121
122      if (Status == EFI_BUFFER_TOO_SMALL) {
123        SysInfo = AllocateZeroPool(SysInfoSize);
124        Status = EfiFpHandle->GetInfo(
125          EfiFpHandle,
126          &gEfiFileSystemInfoGuid,
127          &SysInfoSize,
128          SysInfo);
129      }
130
131      ASSERT_EFI_ERROR(Status);
132
133      gEfiShellProtocol->CloseFile(ShellFileHandle);
134    } else {
135      //
136      // Get the Volume Info from Node->Handle
137      //
138      SysInfo = NULL;
139      SysInfoSize = 0;
140      EfiFpHandle = ConvertShellHandleToEfiFileProtocol(Node->Handle);
141      Status = EfiFpHandle->GetInfo(
142        EfiFpHandle,
143        &gEfiFileSystemInfoGuid,
144        &SysInfoSize,
145        SysInfo);
146
147      if (Status == EFI_BUFFER_TOO_SMALL) {
148        SysInfo = AllocateZeroPool(SysInfoSize);
149        Status = EfiFpHandle->GetInfo(
150          EfiFpHandle,
151          &gEfiFileSystemInfoGuid,
152          &SysInfoSize,
153          SysInfo);
154      }
155
156      ASSERT_EFI_ERROR(Status);
157    }
158
159    ShellPrintHiiEx (
160      -1,
161      -1,
162      NULL,
163      STRING_TOKEN (STR_GEN_SFO_HEADER),
164      gShellLevel2HiiHandle,
165      L"ls");
166    //
167    // print VolumeInfo table
168    //
169    ASSERT(SysInfo != NULL);
170    ShellPrintHiiEx (
171      0,
172      gST->ConOut->Mode->CursorRow,
173      NULL,
174      STRING_TOKEN (STR_LS_SFO_VOLINFO),
175      gShellLevel2HiiHandle,
176      SysInfo->VolumeLabel,
177      SysInfo->VolumeSize,
178      SysInfo->ReadOnly?L"TRUE":L"FALSE",
179      SysInfo->FreeSpace,
180      SysInfo->BlockSize
181     );
182    if (SysInfo != NULL) {
183      FreePool(SysInfo);
184    }
185  }
186
187  if (!Sfo) {
188    //
189    // get directory name from path...
190    //
191    DirectoryName = GetFullyQualifiedPath(CorrectedPath);
192
193    //
194    // print header
195    //
196    ShellPrintHiiEx (
197      0,
198      gST->ConOut->Mode->CursorRow,
199      NULL,
200      STRING_TOKEN (STR_LS_HEADER_LINE1),
201      gShellLevel2HiiHandle,
202      DirectoryName
203     );
204    FreePool(DirectoryName);
205  }
206  for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
207      ; !IsNull(&ListHead->Link, &Node->Link)
208      ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
209     ){
210    ASSERT(Node != NULL);
211    if (LongestPath < StrSize(Node->FullName)) {
212      LongestPath = StrSize(Node->FullName);
213    }
214    ASSERT(Node->Info != NULL);
215    ASSERT((Node->Info->Attribute & EFI_FILE_VALID_ATTR) == Node->Info->Attribute);
216    if (Attribs == 0) {
217      //
218      // NOT system & NOT hidden
219      //
220      if ( (Node->Info->Attribute & EFI_FILE_SYSTEM)
221        || (Node->Info->Attribute & EFI_FILE_HIDDEN)
222       ){
223        continue;
224      }
225    } else if (Attribs != EFI_FILE_VALID_ATTR) {
226      if (Count == 1) {
227        //
228        // the bit must match
229        //
230        if ( (Node->Info->Attribute & Attribs) != Attribs) {
231          continue;
232        }
233      } else {
234        //
235        // exact match on all bits
236        //
237        if ( Node->Info->Attribute != Attribs) {
238          continue;
239        }
240      }
241    }
242
243    if (Sfo) {
244      //
245      // Print the FileInfo Table
246      //
247    ShellPrintHiiEx (
248      0,
249      gST->ConOut->Mode->CursorRow,
250      NULL,
251      STRING_TOKEN (STR_LS_SFO_FILEINFO),
252      gShellLevel2HiiHandle,
253      Node->FullName,
254      Node->Info->FileSize,
255      Node->Info->PhysicalSize,
256      (Node->Info->Attribute & EFI_FILE_ARCHIVE)   != 0?L"a":L"",
257      (Node->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"d":L"",
258      (Node->Info->Attribute & EFI_FILE_HIDDEN)    != 0?L"h":L"",
259      (Node->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L"r":L"",
260      (Node->Info->Attribute & EFI_FILE_SYSTEM)    != 0?L"s":L"",
261      Node->Info->CreateTime.Hour,
262      Node->Info->CreateTime.Minute,
263      Node->Info->CreateTime.Second,
264      Node->Info->CreateTime.Day,
265      Node->Info->CreateTime.Month,
266      Node->Info->CreateTime.Year,
267      Node->Info->LastAccessTime.Hour,
268      Node->Info->LastAccessTime.Minute,
269      Node->Info->LastAccessTime.Second,
270      Node->Info->LastAccessTime.Day,
271      Node->Info->LastAccessTime.Month,
272      Node->Info->LastAccessTime.Year,
273      Node->Info->ModificationTime.Hour,
274      Node->Info->ModificationTime.Minute,
275      Node->Info->ModificationTime.Second,
276      Node->Info->ModificationTime.Day,
277      Node->Info->ModificationTime.Month,
278      Node->Info->ModificationTime.Year
279     );
280    } else {
281      //
282      // print this one out...
283      // first print the universal start, next print the type specific name format, last print the CRLF
284      //
285      ShellPrintHiiEx (
286        -1,
287        -1,
288        NULL,
289        STRING_TOKEN (STR_LS_LINE_START_ALL),
290        gShellLevel2HiiHandle,
291        &Node->Info->ModificationTime,
292        (Node->Info->Attribute & EFI_FILE_DIRECTORY) != 0?L"<DIR>":L"",
293        (Node->Info->Attribute & EFI_FILE_READ_ONLY) != 0?L'r':L' ',
294        Node->Info->FileSize
295       );
296      if (Node->Info->Attribute & EFI_FILE_DIRECTORY) {
297        DirCount++;
298        ShellPrintHiiEx (
299          -1,
300          -1,
301          NULL,
302          STRING_TOKEN (STR_LS_LINE_END_DIR),
303          gShellLevel2HiiHandle,
304          Node->FileName
305         );
306      } else {
307        FileCount++;
308        FileSize += Node->Info->FileSize;
309        if ( (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".nsh", (CHAR16*)&(Node->FileName[StrLen (Node->FileName) - 4])) == 0)
310          || (gUnicodeCollation->StriColl(gUnicodeCollation, (CHAR16*)L".efi", (CHAR16*)&(Node->FileName[StrLen (Node->FileName) - 4])) == 0)
311         ){
312          ShellPrintHiiEx (
313            -1,
314            -1,
315            NULL,
316            STRING_TOKEN (STR_LS_LINE_END_EXE),
317            gShellLevel2HiiHandle,
318            Node->FileName
319           );
320        } else {
321          ShellPrintHiiEx (
322            -1,
323            -1,
324            NULL,
325            STRING_TOKEN (STR_LS_LINE_END_FILE),
326            gShellLevel2HiiHandle,
327            Node->FileName
328           );
329        }
330      }
331    }
332  }
333
334  if (!Sfo) {
335    //
336    // print footer
337    //
338    ShellPrintHiiEx (
339      -1,
340      -1,
341      NULL,
342      STRING_TOKEN (STR_LS_FOOTER_LINE),
343      gShellLevel2HiiHandle,
344      FileCount,
345      FileSize,
346      DirCount
347     );
348  }
349
350  if (Rec){
351    DirectoryName = AllocatePool(LongestPath + 2*sizeof(CHAR16));
352    for ( Node = (EFI_SHELL_FILE_INFO *)GetFirstNode(&ListHead->Link)
353        ; !IsNull(&ListHead->Link, &Node->Link)
354        ; Node = (EFI_SHELL_FILE_INFO *)GetNextNode(&ListHead->Link, &Node->Link)
355       ){
356      //
357      // recurse on any directory except the traversing ones...
358      //
359      if (((Node->Info->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY)
360        && StrCmp(Node->FileName, L".") != 0
361        && StrCmp(Node->FileName, L"..") != 0
362       ){
363        StrCpy(DirectoryName, Node->FullName);
364        StrCat(DirectoryName, L"\\*");
365        PrintLsOutput(
366          Rec,
367          Attribs,
368          Sfo,
369          DirectoryName,
370          FALSE,
371          Count,
372          TimeZone);
373      }
374    }
375    FreePool(DirectoryName);
376  }
377
378  FreePool(CorrectedPath);
379  ShellCloseFileMetaArg(&ListHead);
380  FreePool(ListHead);
381  return (ShellStatus);
382}
383
384STATIC CONST SHELL_PARAM_ITEM LsParamList[] = {
385  {L"-r", TypeFlag},
386  {L"-a", TypeStart},
387  {L"-sfo", TypeFlag},
388  {NULL, TypeMax}
389  };
390
391/**
392  Function for 'ls' command.
393
394  @param[in] ImageHandle  Handle to the Image (NULL if Internal).
395  @param[in] SystemTable  Pointer to the System Table (NULL if Internal).
396**/
397SHELL_STATUS
398EFIAPI
399ShellCommandRunLs (
400  IN EFI_HANDLE        ImageHandle,
401  IN EFI_SYSTEM_TABLE  *SystemTable
402  )
403{
404  EFI_STATUS    Status;
405  LIST_ENTRY    *Package;
406  CHAR16        *ProblemParam;
407  CONST CHAR16  *Attribs;
408  SHELL_STATUS  ShellStatus;
409  UINT64        RequiredAttributes;
410  CONST CHAR16  *PathName;
411  CONST CHAR16  *CurDir;
412  UINTN         Count;
413  CHAR16        *FullPath;
414  UINTN         Size;
415  EFI_TIME      theTime;
416  BOOLEAN       SfoMode;
417
418  Size                = 0;
419  FullPath            = NULL;
420  ProblemParam        = NULL;
421  Attribs             = NULL;
422  ShellStatus         = SHELL_SUCCESS;
423  RequiredAttributes  = 0;
424  PathName            = NULL;
425  CurDir              = NULL;
426  Count               = 0;
427
428  //
429  // initialize the shell lib (we must be in non-auto-init...)
430  //
431  Status = ShellInitialize();
432  ASSERT_EFI_ERROR(Status);
433
434  //
435  // Fix local copies of the protocol pointers
436  //
437  Status = CommandInit();
438  ASSERT_EFI_ERROR(Status);
439
440  //
441  // parse the command line
442  //
443  Status = ShellCommandLineParse (LsParamList, &Package, &ProblemParam, TRUE);
444  if (EFI_ERROR(Status)) {
445    if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
446      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), gShellLevel2HiiHandle, ProblemParam);
447      FreePool(ProblemParam);
448      ShellStatus = SHELL_INVALID_PARAMETER;
449    } else {
450      ASSERT(FALSE);
451    }
452  } else {
453    //
454    // check for "-?"
455    //
456    if (ShellCommandLineGetFlag(Package, L"-?")) {
457      ASSERT(FALSE);
458    }
459
460    if (ShellCommandLineGetCount(Package) > 2) {
461      ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY), gShellLevel2HiiHandle);
462      ShellStatus = SHELL_INVALID_PARAMETER;
463    } else {
464      //
465      // check for -a
466      //
467      if (ShellCommandLineGetFlag(Package, L"-a")) {
468        for ( Attribs = ShellCommandLineGetValue(Package, L"-a")
469            ; Attribs != NULL && *Attribs != CHAR_NULL && ShellStatus == SHELL_SUCCESS
470            ; Attribs++
471           ){
472          switch (*Attribs) {
473            case L'a':
474            case L'A':
475              RequiredAttributes |= EFI_FILE_ARCHIVE;
476              Count++;
477              continue;
478            case L's':
479            case L'S':
480              RequiredAttributes |= EFI_FILE_SYSTEM;
481              Count++;
482              continue;
483            case L'h':
484            case L'H':
485              RequiredAttributes |= EFI_FILE_HIDDEN;
486              Count++;
487              continue;
488            case L'r':
489            case L'R':
490              RequiredAttributes |= EFI_FILE_READ_ONLY;
491              Count++;
492              continue;
493            case L'd':
494            case L'D':
495              RequiredAttributes |= EFI_FILE_DIRECTORY;
496              Count++;
497              continue;
498            default:
499              ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_ATTRIBUTE), gShellLevel2HiiHandle, ShellCommandLineGetValue(Package, L"-a"));
500              ShellStatus = SHELL_INVALID_PARAMETER;
501              break;
502          } // switch
503        } // for loop
504        //
505        // if nothing is specified all are specified
506        //
507        if (RequiredAttributes == 0) {
508          RequiredAttributes = EFI_FILE_VALID_ATTR;
509        }
510      } // if -a present
511      if (ShellStatus == SHELL_SUCCESS) {
512        PathName = ShellCommandLineGetRawValue(Package, 1);
513        if (PathName == NULL) {
514          CurDir = gEfiShellProtocol->GetCurDir(NULL);
515          if (CurDir == NULL) {
516            ShellStatus = SHELL_NOT_FOUND;
517            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_CWD), gShellLevel2HiiHandle);
518          }
519        }
520        if (PathName != NULL) {
521          ASSERT((FullPath == NULL && Size == 0) || (FullPath != NULL));
522          StrnCatGrow(&FullPath, &Size, PathName, 0);
523          if  (ShellIsDirectory(PathName) == EFI_SUCCESS) {
524            StrnCatGrow(&FullPath, &Size, L"\\*", 0);
525          }
526        } else {
527          ASSERT(FullPath == NULL);
528          StrnCatGrow(&FullPath, NULL, L"*", 0);
529        }
530        Status = gRT->GetTime(&theTime, NULL);
531        ASSERT_EFI_ERROR(Status);
532        SfoMode = ShellCommandLineGetFlag(Package, L"-sfo");
533        if (ShellStatus == SHELL_SUCCESS) {
534          ShellStatus = PrintLsOutput(
535            ShellCommandLineGetFlag(Package, L"-r"),
536            RequiredAttributes,
537            SfoMode,
538            FullPath,
539            TRUE,
540            Count,
541            (INT16)(theTime.TimeZone==2047?0:theTime.TimeZone)
542           );
543          if (ShellStatus == SHELL_NOT_FOUND) {
544            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_NO_FILES), gShellLevel2HiiHandle);
545          } else if (ShellStatus == SHELL_INVALID_PARAMETER) {
546            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
547          } else if (ShellStatus != SHELL_SUCCESS) {
548            ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), gShellLevel2HiiHandle);
549          }
550        }
551      }
552    }
553  }
554
555  if (FullPath != NULL) {
556    FreePool(FullPath);
557  }
558  //
559  // free the command line package
560  //
561  ShellCommandLineFreeVarList (Package);
562
563  return (ShellStatus);
564}
565