]>
code.delx.au - refind/blob - refind/mystrings.c
3 * String-manipulation functions
5 * Copyright (c) 2012-2015 Roderick W. Smith
7 * Distributed under the terms of the GNU General Public License (GPL)
8 * version 3 (GPLv3), or (at your option) any later version.
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "mystrings.h"
30 BOOLEAN
StriSubCmp(IN CHAR16
*SmallStr
, IN CHAR16
*BigStr
) {
31 BOOLEAN Found
= 0, Terminate
= 0;
32 UINTN BigIndex
= 0, SmallIndex
= 0, BigStart
= 0;
34 if (SmallStr
&& BigStr
) {
36 if (BigStr
[BigIndex
] == '\0') {
39 if (SmallStr
[SmallIndex
] == '\0') {
43 if ((SmallStr
[SmallIndex
] & ~0x20) == (BigStr
[BigIndex
] & ~0x20)) {
54 } // BOOLEAN StriSubCmp()
56 // Performs a case-insensitive string comparison. This function is necesary
57 // because some EFIs have buggy StriCmp() functions that actually perform
58 // case-sensitive comparisons.
59 // Returns TRUE if strings are identical, FALSE otherwise.
60 BOOLEAN
MyStriCmp(IN CONST CHAR16
*FirstString
, IN CONST CHAR16
*SecondString
) {
61 if (FirstString
&& SecondString
) {
62 while ((*FirstString
!= L
'\0') && ((*FirstString
& ~0x20) == (*SecondString
& ~0x20))) {
66 return (*FirstString
== *SecondString
);
70 } // BOOLEAN MyStriCmp()
74 * Routine Description:
80 * String - Null-terminated string to search.
81 * StrCharSet - Null-terminated string to search for.
84 * The address of the first occurrence of the matching substring if successful, or NULL otherwise.
86 CHAR16
* MyStrStr (IN CHAR16
*String
, IN CHAR16
*StrCharSet
)
91 if ((String
== NULL
) || (StrCharSet
== NULL
))
97 while ((*String
!= L
'\0') && (*StrCharSet
!= L
'\0')) {
98 if (*String
++ != *StrCharSet
) {
105 if (*StrCharSet
== L
'\0') {
110 } // CHAR16 *MyStrStr()
112 // Convert input string to all-lowercase.
113 // DO NOT USE the standard StrLwr() function, since it's broken on some EFIs!
114 VOID
ToLower(CHAR16
* MyString
) {
118 while (MyString
[i
] != L
'\0') {
119 if ((MyString
[i
] >= L
'A') && (MyString
[i
] <= L
'Z'))
120 MyString
[i
] = MyString
[i
] - L
'A' + L
'a';
126 // Merges two strings, creating a new one and returning a pointer to it.
127 // If AddChar != 0, the specified character is placed between the two original
128 // strings (unless the first string is NULL or empty). The original input
129 // string *First is de-allocated and replaced by the new merged string.
130 // This is similar to StrCat, but safer and more flexible because
131 // MergeStrings allocates memory that's the correct size for the
132 // new merged string, so it can take a NULL *First and it cleans
133 // up the old memory. It should *NOT* be used with a constant
134 // *First, though....
135 VOID
MergeStrings(IN OUT CHAR16
**First
, IN CHAR16
*Second
, CHAR16 AddChar
) {
136 UINTN Length1
= 0, Length2
= 0;
140 Length1
= StrLen(*First
);
142 Length2
= StrLen(Second
);
143 NewString
= AllocatePool(sizeof(CHAR16
) * (Length1
+ Length2
+ 2));
144 if (NewString
!= NULL
) {
145 if ((*First
!= NULL
) && (Length1
== 0)) {
149 NewString
[0] = L
'\0';
150 if (*First
!= NULL
) {
151 StrCat(NewString
, *First
);
153 NewString
[Length1
] = AddChar
;
154 NewString
[Length1
+ 1] = '\0';
156 } // if (*First != NULL)
158 StrCat(NewString
, Second
);
162 Print(L
"Error! Unable to allocate memory in MergeStrings()!\n");
164 } // VOID MergeStrings()
166 // Similar to MergeStrings, but breaks the input string into word chunks and
167 // merges each word separately. Words are defined as string fragments separated
168 // by ' ', '_', or '-'.
169 VOID
MergeWords(CHAR16
**MergeTo
, CHAR16
*SourceString
, CHAR16 AddChar
) {
170 CHAR16
*Temp
, *Word
, *p
;
171 BOOLEAN LineFinished
= FALSE
;
174 Temp
= Word
= p
= StrDuplicate(SourceString
);
176 while (!LineFinished
) {
177 if ((*p
== L
' ') || (*p
== L
'_') || (*p
== L
'-') || (*p
== L
'\0')) {
182 MergeStrings(MergeTo
, Word
, AddChar
);
189 Print(L
"Error! Unable to allocate memory in MergeWords()!\n");
192 } // VOID MergeWords()
194 // Restrict TheString to at most Limit characters.
195 // Does this in two ways:
196 // - Locates stretches of two or more spaces and compresses
197 // them down to one space.
198 // - Truncates TheString
199 // Returns TRUE if changes were made, FALSE otherwise
200 BOOLEAN
LimitStringLength(CHAR16
*TheString
, UINTN Limit
) {
201 CHAR16
*SubString
, *TempString
;
203 BOOLEAN HasChanged
= FALSE
;
205 // SubString will be NULL or point WITHIN TheString
206 SubString
= MyStrStr(TheString
, L
" ");
207 while (SubString
!= NULL
) {
209 while (SubString
[i
] == L
' ')
211 if (i
>= StrLen(SubString
)) {
215 TempString
= StrDuplicate(&SubString
[i
]);
216 if (TempString
!= NULL
) {
217 StrCpy(&SubString
[1], TempString
);
218 MyFreePool(TempString
);
221 // memory allocation problem; abort to avoid potentially infinite loop!
225 SubString
= MyStrStr(TheString
, L
" ");
228 // If the string is still too long, truncate it....
229 if (StrLen(TheString
) > Limit
) {
230 TheString
[Limit
] = '\0';
235 } // BOOLEAN LimitStringLength()
237 // Returns all the digits in the input string, including intervening
238 // non-digit characters. For instance, if InString is "foo-3.3.4-7.img",
239 // this function returns "3.3.4-7". If InString contains no digits,
240 // the return value is NULL.
241 CHAR16
*FindNumbers(IN CHAR16
*InString
) {
242 UINTN i
, StartOfElement
, EndOfElement
= 0, CopyLength
;
243 CHAR16
*Found
= NULL
;
245 if (InString
== NULL
)
248 StartOfElement
= StrLen(InString
);
249 // Find start & end of target element
250 for (i
= 0; InString
[i
] != L
'\0'; i
++) {
251 if ((InString
[i
] >= L
'0') && (InString
[i
] <= L
'9')) {
252 if (StartOfElement
> i
)
254 if (EndOfElement
< i
)
258 // Extract the target element
259 if (EndOfElement
> 0) {
260 if (EndOfElement
>= StartOfElement
) {
261 CopyLength
= EndOfElement
- StartOfElement
+ 1;
262 Found
= StrDuplicate(&InString
[StartOfElement
]);
264 Found
[CopyLength
] = 0;
265 } // if (EndOfElement >= StartOfElement)
266 } // if (EndOfElement > 0)
268 } // CHAR16 *FindNumbers()
270 // Find the #Index element (numbered from 0) in a comma-delimited string
272 // Returns the found element, or NULL if Index is out of range or InString
273 // is NULL. Note that the calling function is responsible for freeing the
274 // memory associated with the returned string pointer.
275 CHAR16
*FindCommaDelimited(IN CHAR16
*InString
, IN UINTN Index
) {
276 UINTN StartPos
= 0, CurPos
= 0, InLength
;
277 BOOLEAN Found
= FALSE
;
278 CHAR16
*FoundString
= NULL
;
280 if (InString
!= NULL
) {
281 InLength
= StrLen(InString
);
282 // After while() loop, StartPos marks start of item #Index
283 while ((Index
> 0) && (CurPos
< InLength
)) {
284 if (InString
[CurPos
] == L
',') {
286 StartPos
= CurPos
+ 1;
290 // After while() loop, CurPos is one past the end of the element
291 while ((CurPos
< InLength
) && (!Found
)) {
292 if (InString
[CurPos
] == L
',')
298 FoundString
= StrDuplicate(&InString
[StartPos
]);
299 if (FoundString
!= NULL
)
300 FoundString
[CurPos
- StartPos
] = 0;
302 return (FoundString
);
303 } // CHAR16 *FindCommaDelimited()
305 // Returns TRUE if SmallString is an element in the comma-delimited List,
306 // FALSE otherwise. Performs comparison case-insensitively.
307 BOOLEAN
IsIn(IN CHAR16
*SmallString
, IN CHAR16
*List
) {
309 BOOLEAN Found
= FALSE
;
312 if (SmallString
&& List
) {
313 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
314 if (MyStriCmp(OneElement
, SmallString
))
321 // Returns TRUE if any element of List can be found as a substring of
322 // BigString, FALSE otherwise. Performs comparisons case-insensitively.
323 BOOLEAN
IsInSubstring(IN CHAR16
*BigString
, IN CHAR16
*List
) {
324 UINTN i
= 0, ElementLength
;
325 BOOLEAN Found
= FALSE
;
328 if (BigString
&& List
) {
329 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
330 ElementLength
= StrLen(OneElement
);
331 if ((ElementLength
<= StrLen(BigString
)) && (StriSubCmp(OneElement
, BigString
)))
336 } // BOOLEAN IsSubstringIn()
338 // Returns TRUE if *Input contains nothing but valid hexadecimal characters,
339 // FALSE otherwise. Note that a leading "0x" is NOT acceptable in the input!
340 BOOLEAN
IsValidHex(CHAR16
*Input
) {
341 BOOLEAN IsHex
= TRUE
;
344 while ((Input
[i
] != L
'\0') && IsHex
) {
345 if (!(((Input
[i
] >= L
'0') && (Input
[i
] <= L
'9')) ||
346 ((Input
[i
] >= L
'A') && (Input
[i
] <= L
'F')) ||
347 ((Input
[i
] >= L
'a') && (Input
[i
] <= L
'f')))) {
353 } // BOOLEAN IsValidHex()
355 // Converts consecutive characters in the input string into a
356 // number, interpreting the string as a hexadecimal number, starting
357 // at the specified position and continuing for the specified number
358 // of characters or until the end of the string, whichever is first.
359 // NumChars must be between 1 and 16. Ignores invalid characters.
360 UINT64
StrToHex(CHAR16
*Input
, UINTN Pos
, UINTN NumChars
) {
361 UINT64 retval
= 0x00;
362 UINTN NumDone
= 0, InputLength
;
365 if ((Input
== NULL
) || (NumChars
== 0) || (NumChars
> 16)) {
369 InputLength
= StrLen(Input
);
370 while ((Pos
<= InputLength
) && (NumDone
< NumChars
)) {
372 if ((a
>= '0') && (a
<= '9')) {
377 if ((a
>= 'a') && (a
<= 'f')) {
379 retval
+= (a
- 'a' + 0x0a);
382 if ((a
>= 'A') && (a
<= 'F')) {
384 retval
+= (a
- 'A' + 0x0a);
392 // Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise.
393 // Note that the input string must have no extraneous spaces and must be
394 // conventionally formatted as a 36-character GUID, complete with dashes in
395 // appropriate places.
396 BOOLEAN
IsGuid(CHAR16
*UnknownString
) {
398 BOOLEAN retval
= TRUE
;
401 if (UnknownString
== NULL
)
404 Length
= StrLen(UnknownString
);
408 for (i
= 0; i
< Length
; i
++) {
409 a
= UnknownString
[i
];
410 if ((i
== 8) || (i
== 13) || (i
== 18) || (i
== 23)) {
413 } else if (((a
< L
'a') || (a
> L
'f')) &&
414 ((a
< L
'A') || (a
> L
'F')) &&
415 ((a
< L
'0') && (a
> L
'9'))) {
420 } // BOOLEAN IsGuid()
422 // Return the GUID as a string, suitable for display to the user. Note that the calling
423 // function is responsible for freeing the allocated memory.
424 CHAR16
* GuidAsString(EFI_GUID
*GuidData
) {
427 TheString
= AllocateZeroPool(42 * sizeof(CHAR16
));
428 if (TheString
!= 0) {
429 SPrint (TheString
, 82, L
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
430 (UINTN
)GuidData
->Data1
, (UINTN
)GuidData
->Data2
, (UINTN
)GuidData
->Data3
,
431 (UINTN
)GuidData
->Data4
[0], (UINTN
)GuidData
->Data4
[1], (UINTN
)GuidData
->Data4
[2],
432 (UINTN
)GuidData
->Data4
[3], (UINTN
)GuidData
->Data4
[4], (UINTN
)GuidData
->Data4
[5],
433 (UINTN
)GuidData
->Data4
[6], (UINTN
)GuidData
->Data4
[7]);
436 } // GuidAsString(EFI_GUID *GuidData)
438 EFI_GUID
StringAsGuid(CHAR16
* InString
) {
439 EFI_GUID Guid
= NULL_GUID_VALUE
;
441 if (!IsGuid(InString
)) {
445 Guid
.Data1
= (UINT32
) StrToHex(InString
, 0, 8);
446 Guid
.Data2
= (UINT16
) StrToHex(InString
, 9, 4);
447 Guid
.Data3
= (UINT16
) StrToHex(InString
, 14, 4);
448 Guid
.Data4
[0] = (UINT8
) StrToHex(InString
, 19, 2);
449 Guid
.Data4
[1] = (UINT8
) StrToHex(InString
, 21, 2);
450 Guid
.Data4
[2] = (UINT8
) StrToHex(InString
, 23, 2);
451 Guid
.Data4
[3] = (UINT8
) StrToHex(InString
, 26, 2);
452 Guid
.Data4
[4] = (UINT8
) StrToHex(InString
, 28, 2);
453 Guid
.Data4
[5] = (UINT8
) StrToHex(InString
, 30, 2);
454 Guid
.Data4
[6] = (UINT8
) StrToHex(InString
, 32, 2);
455 Guid
.Data4
[7] = (UINT8
) StrToHex(InString
, 34, 2);
458 } // EFI_GUID StringAsGuid()