]> code.delx.au - refind/blobdiff - refind/mystrings.c
Added forgotten new or split files to git repo.
[refind] / refind / mystrings.c
diff --git a/refind/mystrings.c b/refind/mystrings.c
new file mode 100644 (file)
index 0000000..a5c5823
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * refind/mystrings.c
+ * String-manipulation functions
+ *
+ * Copyright (c) 2012-2015 Roderick W. Smith
+ *
+ * Distributed under the terms of the GNU General Public License (GPL)
+ * version 3 (GPLv3), or (at your option) any later version.
+ *
+ */
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "mystrings.h"
+#include "lib.h"
+
+BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) {
+    BOOLEAN Found = 0, Terminate = 0;
+    UINTN BigIndex = 0, SmallIndex = 0, BigStart = 0;
+
+    if (SmallStr && BigStr) {
+        while (!Terminate) {
+            if (BigStr[BigIndex] == '\0') {
+                Terminate = 1;
+            }
+            if (SmallStr[SmallIndex] == '\0') {
+                Found = 1;
+                Terminate = 1;
+            }
+            if ((SmallStr[SmallIndex] & ~0x20) == (BigStr[BigIndex] & ~0x20)) {
+                SmallIndex++;
+                BigIndex++;
+            } else {
+                SmallIndex = 0;
+                BigStart++;
+                BigIndex = BigStart;
+            }
+        } // while
+    } // if
+    return Found;
+} // BOOLEAN StriSubCmp()
+
+// Performs a case-insensitive string comparison. This function is necesary
+// because some EFIs have buggy StriCmp() functions that actually perform
+// case-sensitive comparisons.
+// Returns TRUE if strings are identical, FALSE otherwise.
+BOOLEAN MyStriCmp(IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString) {
+    if (FirstString && SecondString) {
+        while ((*FirstString != L'\0') && ((*FirstString & ~0x20) == (*SecondString & ~0x20))) {
+                FirstString++;
+                SecondString++;
+        }
+        return (*FirstString == *SecondString);
+    } else {
+        return FALSE;
+    }
+} // BOOLEAN MyStriCmp()
+
+/*++
+ * 
+ * Routine Description:
+ *
+ *  Find a substring.
+ *
+ * Arguments: 
+ *
+ *  String      - Null-terminated string to search.
+ *  StrCharSet  - Null-terminated string to search for.
+ *
+ * Returns:
+ *  The address of the first occurrence of the matching substring if successful, or NULL otherwise.
+ * --*/
+CHAR16* MyStrStr (IN CHAR16  *String, IN CHAR16  *StrCharSet)
+{
+    CHAR16 *Src;
+    CHAR16 *Sub;
+
+    if ((String == NULL) || (StrCharSet == NULL))
+        return NULL;
+
+    Src = String;
+    Sub = StrCharSet;
+
+    while ((*String != L'\0') && (*StrCharSet != L'\0')) {
+        if (*String++ != *StrCharSet) {
+            String = ++Src;
+            StrCharSet = Sub;
+        } else {
+            StrCharSet++;
+        }
+    }
+    if (*StrCharSet == L'\0') {
+        return Src;
+    } else {
+        return NULL;
+    }
+} // CHAR16 *MyStrStr()
+
+// Convert input string to all-lowercase.
+// DO NOT USE the standard StrLwr() function, since it's broken on some EFIs!
+VOID ToLower(CHAR16 * MyString) {
+    UINTN i = 0;
+
+    if (MyString) {
+        while (MyString[i] != L'\0') {
+            if ((MyString[i] >= L'A') && (MyString[i] <= L'Z'))
+                MyString[i] = MyString[i] - L'A' + L'a';
+            i++;
+        } // while
+    } // if
+} // VOID ToLower()
+
+// Merges two strings, creating a new one and returning a pointer to it.
+// If AddChar != 0, the specified character is placed between the two original
+// strings (unless the first string is NULL or empty). The original input
+// string *First is de-allocated and replaced by the new merged string.
+// This is similar to StrCat, but safer and more flexible because
+// MergeStrings allocates memory that's the correct size for the
+// new merged string, so it can take a NULL *First and it cleans
+// up the old memory. It should *NOT* be used with a constant
+// *First, though....
+VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) {
+    UINTN Length1 = 0, Length2 = 0;
+    CHAR16* NewString;
+
+    if (*First != NULL)
+        Length1 = StrLen(*First);
+    if (Second != NULL)
+        Length2 = StrLen(Second);
+    NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2));
+    if (NewString != NULL) {
+        if ((*First != NULL) && (Length1 == 0)) {
+            MyFreePool(*First);
+            *First = NULL;
+        }
+        NewString[0] = L'\0';
+        if (*First != NULL) {
+            StrCat(NewString, *First);
+            if (AddChar) {
+                NewString[Length1] = AddChar;
+                NewString[Length1 + 1] = '\0';
+            } // if (AddChar)
+        } // if (*First != NULL)
+        if (Second != NULL)
+            StrCat(NewString, Second);
+        MyFreePool(*First);
+        *First = NewString;
+    } else {
+        Print(L"Error! Unable to allocate memory in MergeStrings()!\n");
+    } // if/else
+} // VOID MergeStrings()
+
+// Similar to MergeStrings, but breaks the input string into word chunks and
+// merges each word separately. Words are defined as string fragments separated
+// by ' ', '_', or '-'.
+VOID MergeWords(CHAR16 **MergeTo, CHAR16 *SourceString, CHAR16 AddChar) {
+    CHAR16 *Temp, *Word, *p;
+    BOOLEAN LineFinished = FALSE;
+
+    if (SourceString) {
+        Temp = Word = p = StrDuplicate(SourceString);
+        if (Temp) {
+            while (!LineFinished) {
+                if ((*p == L' ') || (*p == L'_') || (*p == L'-') || (*p == L'\0')) {
+                    if (*p == L'\0')
+                        LineFinished = TRUE;
+                    *p = L'\0';
+                    if (*Word != L'\0')
+                        MergeStrings(MergeTo, Word, AddChar);
+                    Word = p + 1;
+                } // if
+                p++;
+            } // while
+            MyFreePool(Temp);
+        } else {
+            Print(L"Error! Unable to allocate memory in MergeWords()!\n");
+        } // if/else
+    } // if
+} // VOID MergeWords()
+
+// Restrict TheString to at most Limit characters.
+// Does this in two ways:
+// - Locates stretches of two or more spaces and compresses
+//   them down to one space.
+// - Truncates TheString
+// Returns TRUE if changes were made, FALSE otherwise
+BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit) {
+    CHAR16    *SubString, *TempString;
+    UINTN     i;
+    BOOLEAN   HasChanged = FALSE;
+
+    // SubString will be NULL or point WITHIN TheString
+    SubString = MyStrStr(TheString, L"  ");
+    while (SubString != NULL) {
+        i = 0;
+        while (SubString[i] == L' ')
+            i++;
+        if (i >= StrLen(SubString)) {
+            SubString[0] = '\0';
+            HasChanged = TRUE;
+        } else {
+            TempString = StrDuplicate(&SubString[i]);
+            if (TempString != NULL) {
+                StrCpy(&SubString[1], TempString);
+                MyFreePool(TempString);
+                HasChanged = TRUE;
+            } else {
+                // memory allocation problem; abort to avoid potentially infinite loop!
+                break;
+            } // if/else
+        } // if/else
+        SubString = MyStrStr(TheString, L"  ");
+    } // while
+
+    // If the string is still too long, truncate it....
+    if (StrLen(TheString) > Limit) {
+        TheString[Limit] = '\0';
+        HasChanged = TRUE;
+    } // if
+
+    return HasChanged;
+} // BOOLEAN LimitStringLength()
+
+// Returns all the digits in the input string, including intervening
+// non-digit characters. For instance, if InString is "foo-3.3.4-7.img",
+// this function returns "3.3.4-7". If InString contains no digits,
+// the return value is NULL.
+CHAR16 *FindNumbers(IN CHAR16 *InString) {
+    UINTN i, StartOfElement, EndOfElement = 0, CopyLength;
+    CHAR16 *Found = NULL;
+
+    if (InString == NULL)
+        return NULL;
+
+    StartOfElement = StrLen(InString);
+    // Find start & end of target element
+    for (i = 0; InString[i] != L'\0'; i++) {
+        if ((InString[i] >= L'0') && (InString[i] <= L'9')) {
+            if (StartOfElement > i)
+                StartOfElement = i;
+            if (EndOfElement < i)
+                EndOfElement = i;
+        } // if
+    } // for
+    // Extract the target element
+    if (EndOfElement > 0) {
+        if (EndOfElement >= StartOfElement) {
+            CopyLength = EndOfElement - StartOfElement + 1;
+            Found = StrDuplicate(&InString[StartOfElement]);
+            if (Found != NULL)
+                Found[CopyLength] = 0;
+        } // if (EndOfElement >= StartOfElement)
+    } // if (EndOfElement > 0)
+    return (Found);
+} // CHAR16 *FindNumbers()
+
+// Find the #Index element (numbered from 0) in a comma-delimited string
+// of elements.
+// Returns the found element, or NULL if Index is out of range or InString
+// is NULL. Note that the calling function is responsible for freeing the
+// memory associated with the returned string pointer.
+CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) {
+    UINTN    StartPos = 0, CurPos = 0, InLength;
+    BOOLEAN  Found = FALSE;
+    CHAR16   *FoundString = NULL;
+
+    if (InString != NULL) {
+        InLength = StrLen(InString);
+        // After while() loop, StartPos marks start of item #Index
+        while ((Index > 0) && (CurPos < InLength)) {
+            if (InString[CurPos] == L',') {
+                Index--;
+                StartPos = CurPos + 1;
+            } // if
+            CurPos++;
+        } // while
+        // After while() loop, CurPos is one past the end of the element
+        while ((CurPos < InLength) && (!Found)) {
+            if (InString[CurPos] == L',')
+                Found = TRUE;
+            else
+                CurPos++;
+        } // while
+        if (Index == 0)
+            FoundString = StrDuplicate(&InString[StartPos]);
+        if (FoundString != NULL)
+            FoundString[CurPos - StartPos] = 0;
+    } // if
+    return (FoundString);
+} // CHAR16 *FindCommaDelimited()
+
+// Returns TRUE if SmallString is an element in the comma-delimited List,
+// FALSE otherwise. Performs comparison case-insensitively.
+BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) {
+   UINTN     i = 0;
+   BOOLEAN   Found = FALSE;
+   CHAR16    *OneElement;
+
+   if (SmallString && List) {
+      while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
+         if (MyStriCmp(OneElement, SmallString))
+            Found = TRUE;
+      } // while
+   } // if
+   return Found;
+} // BOOLEAN IsIn()
+
+// Returns TRUE if any element of List can be found as a substring of
+// BigString, FALSE otherwise. Performs comparisons case-insensitively.
+BOOLEAN IsInSubstring(IN CHAR16 *BigString, IN CHAR16 *List) {
+   UINTN   i = 0, ElementLength;
+   BOOLEAN Found = FALSE;
+   CHAR16  *OneElement;
+
+   if (BigString && List) {
+      while (!Found && (OneElement = FindCommaDelimited(List, i++))) {
+         ElementLength = StrLen(OneElement);
+         if ((ElementLength <= StrLen(BigString)) && (StriSubCmp(OneElement, BigString)))
+            Found = TRUE;
+      } // while
+   } // if
+   return Found;
+} // BOOLEAN IsSubstringIn()
+
+// Returns TRUE if *Input contains nothing but valid hexadecimal characters,
+// FALSE otherwise. Note that a leading "0x" is NOT acceptable in the input!
+BOOLEAN IsValidHex(CHAR16 *Input) {
+    BOOLEAN IsHex = TRUE;
+    UINTN i = 0;
+
+    while ((Input[i] != L'\0') && IsHex) {
+        if (!(((Input[i] >= L'0') && (Input[i] <= L'9')) ||
+              ((Input[i] >= L'A') && (Input[i] <= L'F')) ||
+              ((Input[i] >= L'a') && (Input[i] <= L'f')))) {
+                IsHex = FALSE;
+        }
+        i++;
+    } // while
+    return IsHex;
+} // BOOLEAN IsValidHex()
+
+// Converts consecutive characters in the input string into a
+// number, interpreting the string as a hexadecimal number, starting
+// at the specified position and continuing for the specified number
+// of characters or until the end of the string, whichever is first.
+// NumChars must be between 1 and 16. Ignores invalid characters.
+UINT64 StrToHex(CHAR16 *Input, UINTN Pos, UINTN NumChars) {
+    UINT64 retval = 0x00;
+    UINTN  NumDone = 0, InputLength;
+    CHAR16 a;
+
+    if ((Input == NULL) || (NumChars == 0) || (NumChars > 16)) {
+        return 0;
+    }
+
+    InputLength = StrLen(Input);
+    while ((Pos <= InputLength) && (NumDone < NumChars)) {
+        a = Input[Pos];
+        if ((a >= '0') && (a <= '9')) {
+            retval *= 0x10;
+            retval += (a - '0');
+            NumDone++;
+        }
+        if ((a >= 'a') && (a <= 'f')) {
+            retval *= 0x10;
+            retval += (a - 'a' + 0x0a);
+            NumDone++;
+        }
+        if ((a >= 'A') && (a <= 'F')) {
+            retval *= 0x10;
+            retval += (a - 'A' + 0x0a);
+            NumDone++;
+        }
+        Pos++;
+    } // while()
+    return retval;
+} // StrToHex()
+
+// Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise.
+// Note that the input string must have no extraneous spaces and must be
+// conventionally formatted as a 36-character GUID, complete with dashes in
+// appropriate places.
+BOOLEAN IsGuid(CHAR16 *UnknownString) {
+    UINTN   Length, i;
+    BOOLEAN retval = TRUE;
+    CHAR16  a;
+
+    if (UnknownString == NULL)
+        return FALSE;
+
+    Length = StrLen(UnknownString);
+    if (Length != 36)
+        return FALSE;
+
+    for (i = 0; i < Length; i++) {
+        a = UnknownString[i];
+        if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
+            if (a != L'-')
+                retval = FALSE;
+        } else if (((a < L'a') || (a > L'f')) &&
+                   ((a < L'A') || (a > L'F')) &&
+                   ((a < L'0') && (a > L'9'))) {
+            retval = FALSE;
+        } // if/else if
+    } // for
+    return retval;
+} // BOOLEAN IsGuid()
+
+// Return the GUID as a string, suitable for display to the user. Note that the calling
+// function is responsible for freeing the allocated memory.
+CHAR16 * GuidAsString(EFI_GUID *GuidData) {
+   CHAR16 *TheString;
+
+   TheString = AllocateZeroPool(42 * sizeof(CHAR16));
+   if (TheString != 0) {
+      SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+              (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3,
+              (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2],
+              (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5],
+              (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]);
+   }
+   return TheString;
+} // GuidAsString(EFI_GUID *GuidData)
+
+EFI_GUID StringAsGuid(CHAR16 * InString) {
+    EFI_GUID  Guid = NULL_GUID_VALUE;
+
+    if (!IsGuid(InString)) {
+        return Guid;
+    }
+
+    Guid.Data1 = (UINT32) StrToHex(InString, 0, 8);
+    Guid.Data2 = (UINT16) StrToHex(InString, 9, 4);
+    Guid.Data3 = (UINT16) StrToHex(InString, 14, 4);
+    Guid.Data4[0] = (UINT8) StrToHex(InString, 19, 2);
+    Guid.Data4[1] = (UINT8) StrToHex(InString, 21, 2);
+    Guid.Data4[2] = (UINT8) StrToHex(InString, 23, 2);
+    Guid.Data4[3] = (UINT8) StrToHex(InString, 26, 2);
+    Guid.Data4[4] = (UINT8) StrToHex(InString, 28, 2);
+    Guid.Data4[5] = (UINT8) StrToHex(InString, 30, 2);
+    Guid.Data4[6] = (UINT8) StrToHex(InString, 32, 2);
+    Guid.Data4[7] = (UINT8) StrToHex(InString, 34, 2);
+
+    return Guid;
+} // EFI_GUID StringAsGuid()