From b1f95a06ca1a66ad15b3d8843fb4c7563c5350d2 Mon Sep 17 00:00:00 2001 From: jcarsey Date: Tue, 16 Jun 2009 00:23:19 +0000 Subject: Updating with new functions and adding "C" style entrypoint library with example application. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@8564 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Library/BaseFileHandleLib/BaseFileHandleLib.c | 304 ++++++++++++++++++++- .../UefiShellCEntryLib/UefiShellCEntryLib.c | 82 ++++++ .../UefiShellCEntryLib/UefiShellCEntryLib.inf | 47 ++++ ShellPkg/Library/UefiShellLib/UefiShellLib.c | 145 +++++++--- ShellPkg/Library/UefiShellLib/UefiShellLib.inf | 3 + 5 files changed, 544 insertions(+), 37 deletions(-) create mode 100644 ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c create mode 100644 ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf (limited to 'ShellPkg/Library') diff --git a/ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.c b/ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.c index 87b5772488..023210e8e0 100644 --- a/ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.c +++ b/ShellPkg/Library/BaseFileHandleLib/BaseFileHandleLib.c @@ -14,11 +14,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include -#include +#include + +#include + #include #include - -#include +#include +#include #define MAX_FILE_NAME_LEN 522 // (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes) #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN) @@ -620,4 +623,297 @@ FileHandleGetSize ( FreePool(FileInfo); return (EFI_SUCCESS); -} \ No newline at end of file +} + + +/** + Safely append (on the left) with automatic string resizing given length of Destination and + desired length of copy from Source. + + append the first D characters of Source to the end of Destination, where D is + the lesser of Count and the StrLen() of Source. If appending those D characters + will fit within Destination (whose Size is given as CurrentSize) and + still leave room for a null terminator, then those characters are appended, + starting at the original terminating null of Destination, and a new terminating + null is appended. + + If appending D characters onto Destination will result in a overflow of the size + given in CurrentSize the string will be grown such that the copy can be performed + and CurrentSize will be updated to the new size. + + If Source is NULL, there is nothing to append, just return the current buffer in + Destination. + + if Destination is NULL, then ASSERT() + if Destination's current length (including NULL terminator) is already more then + CurrentSize, then ASSERT() + + @param[in][out] Destination The String to append onto + @param[in][out] CurrentSize on call the number of bytes in Destination. On + return possibly the new size (still in bytes). if NULL + then allocate whatever is needed. + @param[in] Source The String to append from + @param[in] Count Maximum number of characters to append. if 0 then + all are appended. + + @return Destination return the resultant string. +**/ +CHAR16* +EFIAPI +StrnCatGrowLeft ( + IN OUT CHAR16 **Destination, + IN OUT UINTN *CurrentSize, + IN CONST CHAR16 *Source, + IN UINTN Count + ){ + UINTN DestinationStartSize; + UINTN NewSize; + + // + // ASSERTs + // + ASSERT(Destination != NULL); + + // + // If there's nothing to do then just return Destination + // + if (Source == NULL) { + return (*Destination); + } + + // + // allow for NULL pointers address as Destination + // + if (*Destination != NULL) { + ASSERT(CurrentSize != 0); + DestinationStartSize = StrSize(*Destination); + ASSERT(DestinationStartSize <= *CurrentSize); + } else { + DestinationStartSize = 0; +// ASSERT(*CurrentSize == 0); + } + + // + // Append all of Source? + // + if (Count == 0) { + Count = StrLen(Source); + } + + // + // Test and grow if required + // + if (CurrentSize != NULL) { + NewSize = *CurrentSize; + while (NewSize < (DestinationStartSize + (Count*sizeof(CHAR16)))) { + NewSize += 2 * Count * sizeof(CHAR16); + } + *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination); + *CurrentSize = NewSize; + } else { + *Destination = AllocateZeroPool((Count+1)*sizeof(CHAR16)); + } + + *Destination = CopyMem(*Destination+StrLen(Source), *Destination, StrSize(*Destination)); + *Destination = CopyMem(*Destination, Source, StrLen(Source)); + return (*Destination); +} + +/** + Function to get a full filename given a EFI_FILE_HANDLE somewhere lower on the + directory 'stack'. + + if Handle is NULL, return EFI_INVALID_PARAMETER + + @param[in] Handle Handle to the Directory or File to create path to. + @param[out] FullFileName pointer to pointer to generated full file name. It + is the responsibility of the caller to free this memory + with a call to FreePool(). + @retval EFI_SUCCESS the operation was sucessful and the FullFileName is valid. + @retval EFI_INVALID_PARAMETER Handle was NULL. + @retval EFI_INVALID_PARAMETER FullFileName was NULL. + @retval EFI_OUT_OF_RESOURCES a memory allocation failed. +**/ +EFI_STATUS +EFIAPI +FileHandleGetFileName ( + IN CONST EFI_FILE_HANDLE Handle, + OUT CHAR16 **FullFileName + ){ + EFI_STATUS Status; + UINTN Size; + EFI_FILE_HANDLE CurrentHandle; + EFI_FILE_HANDLE NextHigherHandle; + EFI_FILE_INFO *FileInfo; + + Size = 0; + *FullFileName = NULL; + + // + // Check our parameters + // + if (FullFileName == NULL || Handle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + Status = Handle->Open(Handle, &CurrentHandle, L".", EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR(Status)) { + // + // Reverse out the current directory on the device + // + for (;;) { + FileInfo = FileHandleGetInfo(CurrentHandle); + if (FileInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } else { + // + // We got info... do we have a name? if yes preceed the current path with it... + // + if (StrLen (FileInfo->FileName) == 0) { + *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0); + FreePool(FileInfo); + break; + } else { + *FullFileName = StrnCatGrowLeft(FullFileName, &Size, FileInfo->FileName, 0); + *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0); + FreePool(FileInfo); + } + } + // + // Move to the parent directory + // + Status = CurrentHandle->Open (CurrentHandle, &NextHigherHandle, L"..", EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + break; + } + + FileHandleClose(CurrentHandle); + CurrentHandle = NextHigherHandle; + } + } + + if (CurrentHandle != NULL) { + CurrentHandle->Close (CurrentHandle); + } + + if (EFI_ERROR(Status) && *FullFileName != NULL) { + FreePool(FullFileName); + } + + return (Status); +} + +/** + Function to read a single line (up to but not including the \n) from a file. + + @param[in] Handle FileHandle to read from + @param[in][out] Buffer pointer to buffer to read into + @param[in][out] Size pointer to number of bytes in buffer + @param[in[ Truncate if TRUE then allows for truncation of the line to fit. + if FALSE will reset the position to the begining of the + line if the buffer is not large enough. + + @retval EFI_SUCCESS the operation was sucessful. the line is stored in + Buffer. + @retval EFI_INVALID_PARAMETER Handle was NULL. + @retval EFI_INVALID_PARAMETER Buffer was NULL. + @retval EFI_INVALID_PARAMETER Size was NULL. + @retval EFI_BUFFER_TOO_SMALL Size was not enough space to store the line. + Size was updated to minimum space required. + @sa FileHandleRead +**/ +EFI_STATUS +EFIAPI +FileHandleReadLine( + IN EFI_FILE_HANDLE Handle, + IN OUT VOID *Buffer, + IN OUT UINTN *Size, + IN BOOLEAN Truncate + ){ + EFI_STATUS Status; + CHAR16 CharBuffer; + UINTN CharSize; + UINTN CountSoFar; + UINT64 Position; + + + if (Handle == NULL + ||Buffer == NULL + ||Size == NULL + ){ + return (EFI_INVALID_PARAMETER); + } + FileHandleGetPosition(Handle, &Position); + + for (CountSoFar = 0;;CountSoFar++){ + CharSize = sizeof(CharBuffer); + Status = FileHandleRead(Handle, &CharSize, &CharBuffer); + if ( EFI_ERROR(Status) + || CharSize == 0 + || CharBuffer == '\n' + ){ + break; + } + // + // if we have space save it... + // + if ((CountSoFar+1)*sizeof(CHAR16) < *Size){ + ((CHAR16*)Buffer)[CountSoFar] = CharBuffer; + ((CHAR16*)Buffer)[CountSoFar+1] = '\0'; + } + } + + // + // if we ran out of space tell when... + // + if ((CountSoFar+1)*sizeof(CHAR16) > *Size){ + *Size = (CountSoFar+1)*sizeof(CHAR16); + if (Truncate == FALSE) { + FileHandleSetPosition(Handle, Position); + } else { + DEBUG((DEBUG_WARN, "The line was truncated in ReadLine")); + } + return (EFI_BUFFER_TOO_SMALL); + } + *Size = (CountSoFar+1)*sizeof(CHAR16); + return (Status); +} + +/** + function to write a line of unicode text to a file. + + if Handle is NULL, ASSERT. + if Buffer is NULL, do nothing. (return SUCCESS) + + @param[in] Handle FileHandle to write to + @param[in] Buffer Buffer to write + + @retval EFI_SUCCESS the data was written. + @retval other failure. + + @sa FileHandleWrite +**/ +EFI_STATUS +EFIAPI +FileHandleWriteLine( + IN EFI_FILE_HANDLE Handle, + IN CHAR16 *Buffer + ){ + EFI_STATUS Status; + UINTN Size; + + ASSERT(Handle != NULL); + + if (Buffer == NULL) { + return (EFI_SUCCESS); + } + + Size = StrLen(Buffer); + Status = FileHandleWrite(Handle, &Size, Buffer); + if (EFI_ERROR(Status)) { + return (Status); + } + Size = StrLen(L"\r\n"); + return FileHandleWrite(Handle, &Size, L"\r\n"); +} diff --git a/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c b/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c new file mode 100644 index 0000000000..0ce5271f22 --- /dev/null +++ b/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c @@ -0,0 +1,82 @@ +/** @file + Provides application point extension for "C" style main funciton + +Copyright (c) 2009, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include + +#include + +INT32 +EFIAPI +main( + UINTN Argc, + CHAR16 **Argv +); + +EFI_STATUS +EFIAPI +ShellCEntryLib( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ){ + INT32 ReturnFromMain; + EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol; + EFI_SHELL_INTERFACE *EfiShellInterface; + EFI_STATUS Status; + + ReturnFromMain = -1; + EfiShellParametersProtocol = NULL; + EfiShellInterface = NULL; + + Status = SystemTable->BootServices->OpenProtocol(ImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&EfiShellParametersProtocol, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR(Status)) { + // + // use shell 2.0 interface + // + ReturnFromMain = main(EfiShellInterface->Argc, EfiShellInterface->Argv); + } else { + // + // try to get shell 1.0 interface instead. + // + Status = SystemTable->BootServices->OpenProtocol(ImageHandle, + &gEfiShellInterfaceGuid, + (VOID **)&EfiShellInterface, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR(Status)) { + // + // use shell 1.0 interface + // + ReturnFromMain = main(EfiShellParametersProtocol->Argc, EfiShellParametersProtocol->Argv); + } else { + ASSERT(FALSE); + } + } + if (ReturnFromMain == 0) { + return (EFI_SUCCESS); + } else { + return (EFI_UNSUPPORTED); + } +} diff --git a/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf b/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf new file mode 100644 index 0000000000..15a744ff88 --- /dev/null +++ b/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf @@ -0,0 +1,47 @@ +#/** @file +# Provides interface to shell functionality for shell commands and applications. +# +# Copyright (c) 2006 - 2009, Intel Corporation. +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = UefiShellCEntryLib + FILE_GUID = 0e205c8a-8586-4dec-9f5c-4f9e394aefe8 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = ShellCEntryLib|UEFI_APPLICATION + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + UefiShellCEntryLib.c + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + DebugLib + + +[Protocols] + gEfiShellParametersProtocolGuid # ALWAYS_CONSUMED + gEfiShellInterfaceGuid # SOMETIMES_CONSUMED + +[Guids] + +[Pcd.common] + diff --git a/ShellPkg/Library/UefiShellLib/UefiShellLib.c b/ShellPkg/Library/UefiShellLib/UefiShellLib.c index 1ddff9a2a0..7ac7765b4b 100644 --- a/ShellPkg/Library/UefiShellLib/UefiShellLib.c +++ b/ShellPkg/Library/UefiShellLib/UefiShellLib.c @@ -22,13 +22,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include +#include + #include #include #include #include #include -#include "BaseShellLib.h" +#include "UefiShellLib.h" #define MAX_FILE_NAME_LEN 522 // (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes) #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN) @@ -237,7 +240,6 @@ ShellLibConstructor ( mEfiShellInterface = NULL; mEfiShellEnvironment2Handle = NULL; - ///@todo make a worker constructor so initialize function works // // verify that auto initialize is not set false // @@ -455,7 +457,7 @@ ShellOpenFileByDevicePath( } Status = gBS->OpenProtocol(*DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, - (VOID**) &EfiSimpleFileSystemProtocol, + (VOID**)&EfiSimpleFileSystemProtocol, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); @@ -569,6 +571,8 @@ ShellOpenFileByName( { EFI_HANDLE DeviceHandle; EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; // // ASSERT if FileName is NULL @@ -579,11 +583,16 @@ ShellOpenFileByName( // // Use UEFI Shell 2.0 method // - return (mEfiShellProtocol->OpenFileByName(FileName, - FileHandle, - OpenMode)); - - ///@todo add the attributes + Status = mEfiShellProtocol->OpenFileByName(FileName, + FileHandle, + OpenMode); + if (!EFI_ERROR(Status)){ + FileInfo = FileHandleGetInfo(*FileHandle); + ASSERT(FileInfo != NULL); + FileInfo->Attribute = Attributes; + Status = FileHandleSetInfo(*FileHandle, FileInfo); + } + return (Status); } // // Using EFI Shell version @@ -1602,29 +1611,6 @@ InternalCommandLineParse ( // // do nothing for NULL argv // - } else if (GetItemValue == TRUE) { - ASSERT(CurrentItemPackage != NULL); - // - // get the item VALUE for the previous flag - // - GetItemValue = FALSE; - CurrentItemPackage->Value = AllocateZeroPool(StrSize(Argv[LoopCounter])); - ASSERT(CurrentItemPackage->Value != NULL); - StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]); - InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); - } else if (InternalIsFlag(Argv[LoopCounter]) == FALSE) { - // - // add this one as a non-flag - // - CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE)); - ASSERT(CurrentItemPackage != NULL); - CurrentItemPackage->Name = NULL; - CurrentItemPackage->Type = TypePosition; - CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter])); - ASSERT(CurrentItemPackage->Value != NULL); - StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]); - CurrentItemPackage->OriginalPosition = Count++; - InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType) == TRUE) { // // this is a flag @@ -1636,6 +1622,7 @@ InternalCommandLineParse ( StrCpy(CurrentItemPackage->Name, Argv[LoopCounter]); CurrentItemPackage->Type = CurrentItemType; CurrentItemPackage->OriginalPosition = (UINTN)(-1); + CurrentItemPackage->Value = NULL; // // Does this flag require a value @@ -1649,9 +1636,31 @@ InternalCommandLineParse ( // // this item has no value expected; we are done // - CurrentItemPackage->Value = NULL; - InsertTailList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); + InsertHeadList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); } + } else if (GetItemValue == TRUE && InternalIsFlag(Argv[LoopCounter]) == FALSE) { + ASSERT(CurrentItemPackage != NULL); + // + // get the item VALUE for the previous flag + // + GetItemValue = FALSE; + CurrentItemPackage->Value = AllocateZeroPool(StrSize(Argv[LoopCounter])); + ASSERT(CurrentItemPackage->Value != NULL); + StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]); + InsertHeadList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); + } else if (InternalIsFlag(Argv[LoopCounter]) == FALSE) { + // + // add this one as a non-flag + // + CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE)); + ASSERT(CurrentItemPackage != NULL); + CurrentItemPackage->Name = NULL; + CurrentItemPackage->Type = TypePosition; + CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter])); + ASSERT(CurrentItemPackage->Value != NULL); + StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]); + CurrentItemPackage->OriginalPosition = Count++; + InsertHeadList(*CheckPackage, (LIST_ENTRY*)CurrentItemPackage); } else if (ProblemParam) { // // this was a non-recognised flag... error! @@ -1936,4 +1945,74 @@ ShellCommandLineGetRawValue ( } } return (NULL); +} + +/** + Print at a specific location on the screen. + + This function will move the cursor to a given screen location, print the specified string, + and return the cursor to the original locaiton. + + If -1 is specified for either the Row or Col the current screen location for BOTH + will be used and the cursor's position will not be moved back to an original location. + + if either Row or Col is out of range for the current console, then ASSERT + if Format is NULL, then ASSERT + + In addition to the standard %-based flags as supported by UefiLib Print() this supports + the following additional flags: + %N - Set output attribute to normal + %H - Set output attribute to highlight + %E - Set output attribute to error + %B - Set output attribute to blue color + %V - Set output attribute to green color + + Note: The background color is controlled by the shell command cls. + + @param[in] Row the row to print at + @param[in] Col the column to print at + @param[in] Format the format string + + @return the number of characters printed to the screen +**/ + +UINTN +EFIAPI +ShellPrintEx( + IN INT32 Col OPTIONAL, + IN INT32 Row OPTIONAL, + IN CONST CHAR16 *Format, + ... + ){ + VA_LIST Marker; + UINTN BufferSize; + CHAR16 *Buffer; + UINTN Return; + INT32 CurrentCol; + INT32 CurrentRow; + EFI_STATUS Status; + + BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16); + Buffer = AllocateZeroPool (BufferSize); + ASSERT (Buffer != NULL); + + VA_START (Marker, Format); + Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker); + + if (Col != -1 && Row != -1) { + CurrentCol = gST->ConOut->Mode->CursorColumn; + CurrentRow = gST->ConOut->Mode->CursorRow; + Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row); + ASSERT_EFI_ERROR(Status); + Status = gST->ConOut->OutputString(gST->ConOut, Buffer); + ASSERT_EFI_ERROR(Status); + Status = gST->ConOut->SetCursorPosition(gST->ConOut, CurrentCol, CurrentRow); + ASSERT_EFI_ERROR(Status); + } else { + Status = gST->ConOut->OutputString(gST->ConOut, Buffer); + ASSERT_EFI_ERROR(Status); + } + + FreePool(Buffer); + return (Return); } \ No newline at end of file diff --git a/ShellPkg/Library/UefiShellLib/UefiShellLib.inf b/ShellPkg/Library/UefiShellLib/UefiShellLib.inf index 5ed4259479..c2f26cd526 100644 --- a/ShellPkg/Library/UefiShellLib/UefiShellLib.inf +++ b/ShellPkg/Library/UefiShellLib/UefiShellLib.inf @@ -43,6 +43,8 @@ BaseMemoryLib DebugLib FileHandleLib + PrintLib + UefiLib [Protocols] gEfiSimpleFileSystemProtocolGuid # ALWAYS_CONSUMED @@ -61,3 +63,4 @@ [Pcd.common] gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize # ALWAYS_CONSUMED + gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize # ALWAYS_CONSUMED -- cgit v1.2.3