]>
code.delx.au - refind/blob - refind/mystrings.c
539aba20bd0fac540949f319ff41725379192524
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 // As a special case for Arch Linux the strings "linux" and "linux-lts"
242 // are considered to be digits.
243 CHAR16
*FindNumbers(IN CHAR16
*InString
) {
244 UINTN i
, StartOfElement
= 0, EndOfElement
= 0, CopyLength
;
245 CHAR16
*Found
= NULL
;
247 if (InString
== NULL
)
250 // Find "linux-lts" or "linux"
251 Found
= MyStrStr(InString
, L
"linux-lts");
253 StartOfElement
= Found
- InString
;
254 EndOfElement
= StartOfElement
+ StrLen(L
"linux-lts") - 1;
256 Found
= MyStrStr(InString
, L
"linux");
258 StartOfElement
= Found
- InString
;
259 EndOfElement
= StartOfElement
+ StrLen(L
"linux") - 1;
264 // Find start & end of target element
265 for (i
= 0; InString
[i
] != L
'\0'; i
++) {
266 if ((InString
[i
] >= L
'0') && (InString
[i
] <= L
'9')) {
267 if (StartOfElement
> i
)
269 if (EndOfElement
< i
)
274 // Extract the target element
275 if (EndOfElement
> 0) {
276 if (EndOfElement
>= StartOfElement
) {
277 CopyLength
= EndOfElement
- StartOfElement
+ 1;
278 Found
= StrDuplicate(&InString
[StartOfElement
]);
280 Found
[CopyLength
] = 0;
281 } // if (EndOfElement >= StartOfElement)
282 } // if (EndOfElement > 0)
284 } // CHAR16 *FindNumbers()
286 // Returns the number of characters that are in common between
287 // String1 and String2 before they diverge. For instance, if
288 // String1 is "FooBar" and String2 is "FoodiesBar", this function
289 // will return "3", since they both start with "Foo".
290 UINTN
NumCharsInCommon(IN CHAR16
* String1
, IN CHAR16
* String2
) {
292 if ((String1
== NULL
) || (String2
== NULL
))
294 while ((String1
[Count
] != L
'\0') && (String2
[Count
] != L
'\0') && (String1
[Count
] == String2
[Count
]))
297 } // UINTN NumCharsInCommon()
299 // Find the #Index element (numbered from 0) in a comma-delimited string
301 // Returns the found element, or NULL if Index is out of range or InString
302 // is NULL. Note that the calling function is responsible for freeing the
303 // memory associated with the returned string pointer.
304 CHAR16
*FindCommaDelimited(IN CHAR16
*InString
, IN UINTN Index
) {
305 UINTN StartPos
= 0, CurPos
= 0, InLength
;
306 BOOLEAN Found
= FALSE
;
307 CHAR16
*FoundString
= NULL
;
309 if (InString
!= NULL
) {
310 InLength
= StrLen(InString
);
311 // After while() loop, StartPos marks start of item #Index
312 while ((Index
> 0) && (CurPos
< InLength
)) {
313 if (InString
[CurPos
] == L
',') {
315 StartPos
= CurPos
+ 1;
319 // After while() loop, CurPos is one past the end of the element
320 while ((CurPos
< InLength
) && (!Found
)) {
321 if (InString
[CurPos
] == L
',')
327 FoundString
= StrDuplicate(&InString
[StartPos
]);
328 if (FoundString
!= NULL
)
329 FoundString
[CurPos
- StartPos
] = 0;
331 return (FoundString
);
332 } // CHAR16 *FindCommaDelimited()
334 // Returns TRUE if SmallString is an element in the comma-delimited List,
335 // FALSE otherwise. Performs comparison case-insensitively.
336 BOOLEAN
IsIn(IN CHAR16
*SmallString
, IN CHAR16
*List
) {
338 BOOLEAN Found
= FALSE
;
341 if (SmallString
&& List
) {
342 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
343 if (MyStriCmp(OneElement
, SmallString
))
350 // Returns TRUE if any element of List can be found as a substring of
351 // BigString, FALSE otherwise. Performs comparisons case-insensitively.
352 BOOLEAN
IsInSubstring(IN CHAR16
*BigString
, IN CHAR16
*List
) {
353 UINTN i
= 0, ElementLength
;
354 BOOLEAN Found
= FALSE
;
357 if (BigString
&& List
) {
358 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
359 ElementLength
= StrLen(OneElement
);
360 if ((ElementLength
<= StrLen(BigString
)) && (StriSubCmp(OneElement
, BigString
)))
365 } // BOOLEAN IsSubstringIn()
367 // Replace *SearchString in **MainString with *ReplString -- but if *SearchString
368 // is preceded by "%", instead remove that character.
369 // Returns TRUE if replacement was done, FALSE otherwise.
370 BOOLEAN
ReplaceSubstring(IN OUT CHAR16
**MainString
, IN CHAR16
*SearchString
, IN CHAR16
*ReplString
) {
371 BOOLEAN WasReplaced
= FALSE
;
372 CHAR16
*FoundSearchString
, *NewString
, *EndString
;
374 FoundSearchString
= MyStrStr(*MainString
, SearchString
);
375 if (FoundSearchString
) {
376 NewString
= AllocateZeroPool(sizeof(CHAR16
) * StrLen(*MainString
));
378 EndString
= &(FoundSearchString
[StrLen(SearchString
)]);
379 FoundSearchString
[0] = L
'\0';
380 if ((FoundSearchString
> *MainString
) && (FoundSearchString
[-1] == L
'%')) {
381 FoundSearchString
[-1] = L
'\0';
382 ReplString
= SearchString
;
384 StrCpy(NewString
, *MainString
);
385 MergeStrings(&NewString
, ReplString
, L
'\0');
386 MergeStrings(&NewString
, EndString
, L
'\0');
387 MyFreePool(MainString
);
388 *MainString
= NewString
;
393 } // BOOLEAN ReplaceSubstring()
395 // Returns TRUE if *Input contains nothing but valid hexadecimal characters,
396 // FALSE otherwise. Note that a leading "0x" is NOT acceptable in the input!
397 BOOLEAN
IsValidHex(CHAR16
*Input
) {
398 BOOLEAN IsHex
= TRUE
;
401 while ((Input
[i
] != L
'\0') && IsHex
) {
402 if (!(((Input
[i
] >= L
'0') && (Input
[i
] <= L
'9')) ||
403 ((Input
[i
] >= L
'A') && (Input
[i
] <= L
'F')) ||
404 ((Input
[i
] >= L
'a') && (Input
[i
] <= L
'f')))) {
410 } // BOOLEAN IsValidHex()
412 // Converts consecutive characters in the input string into a
413 // number, interpreting the string as a hexadecimal number, starting
414 // at the specified position and continuing for the specified number
415 // of characters or until the end of the string, whichever is first.
416 // NumChars must be between 1 and 16. Ignores invalid characters.
417 UINT64
StrToHex(CHAR16
*Input
, UINTN Pos
, UINTN NumChars
) {
418 UINT64 retval
= 0x00;
419 UINTN NumDone
= 0, InputLength
;
422 if ((Input
== NULL
) || (NumChars
== 0) || (NumChars
> 16)) {
426 InputLength
= StrLen(Input
);
427 while ((Pos
<= InputLength
) && (NumDone
< NumChars
)) {
429 if ((a
>= '0') && (a
<= '9')) {
434 if ((a
>= 'a') && (a
<= 'f')) {
436 retval
+= (a
- 'a' + 0x0a);
439 if ((a
>= 'A') && (a
<= 'F')) {
441 retval
+= (a
- 'A' + 0x0a);
449 // Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise.
450 // Note that the input string must have no extraneous spaces and must be
451 // conventionally formatted as a 36-character GUID, complete with dashes in
452 // appropriate places.
453 BOOLEAN
IsGuid(CHAR16
*UnknownString
) {
455 BOOLEAN retval
= TRUE
;
458 if (UnknownString
== NULL
)
461 Length
= StrLen(UnknownString
);
465 for (i
= 0; i
< Length
; i
++) {
466 a
= UnknownString
[i
];
467 if ((i
== 8) || (i
== 13) || (i
== 18) || (i
== 23)) {
470 } else if (((a
< L
'a') || (a
> L
'f')) &&
471 ((a
< L
'A') || (a
> L
'F')) &&
472 ((a
< L
'0') && (a
> L
'9'))) {
477 } // BOOLEAN IsGuid()
479 // Return the GUID as a string, suitable for display to the user. Note that the calling
480 // function is responsible for freeing the allocated memory.
481 CHAR16
* GuidAsString(EFI_GUID
*GuidData
) {
484 TheString
= AllocateZeroPool(42 * sizeof(CHAR16
));
485 if (TheString
!= 0) {
486 SPrint (TheString
, 82, L
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
487 (UINTN
)GuidData
->Data1
, (UINTN
)GuidData
->Data2
, (UINTN
)GuidData
->Data3
,
488 (UINTN
)GuidData
->Data4
[0], (UINTN
)GuidData
->Data4
[1], (UINTN
)GuidData
->Data4
[2],
489 (UINTN
)GuidData
->Data4
[3], (UINTN
)GuidData
->Data4
[4], (UINTN
)GuidData
->Data4
[5],
490 (UINTN
)GuidData
->Data4
[6], (UINTN
)GuidData
->Data4
[7]);
493 } // GuidAsString(EFI_GUID *GuidData)
495 EFI_GUID
StringAsGuid(CHAR16
* InString
) {
496 EFI_GUID Guid
= NULL_GUID_VALUE
;
498 if (!IsGuid(InString
)) {
502 Guid
.Data1
= (UINT32
) StrToHex(InString
, 0, 8);
503 Guid
.Data2
= (UINT16
) StrToHex(InString
, 9, 4);
504 Guid
.Data3
= (UINT16
) StrToHex(InString
, 14, 4);
505 Guid
.Data4
[0] = (UINT8
) StrToHex(InString
, 19, 2);
506 Guid
.Data4
[1] = (UINT8
) StrToHex(InString
, 21, 2);
507 Guid
.Data4
[2] = (UINT8
) StrToHex(InString
, 23, 2);
508 Guid
.Data4
[3] = (UINT8
) StrToHex(InString
, 26, 2);
509 Guid
.Data4
[4] = (UINT8
) StrToHex(InString
, 28, 2);
510 Guid
.Data4
[5] = (UINT8
) StrToHex(InString
, 30, 2);
511 Guid
.Data4
[6] = (UINT8
) StrToHex(InString
, 32, 2);
512 Guid
.Data4
[7] = (UINT8
) StrToHex(InString
, 34, 2);
515 } // EFI_GUID StringAsGuid()
517 // Delete the STRING_LIST pointed to by *StringList.
518 VOID
DeleteStringList(STRING_LIST
*StringList
) {
519 STRING_LIST
*Current
= StringList
, *Previous
;
521 while (Current
!= NULL
) {
522 MyFreePool(Current
->Value
);
524 Current
= Current
->Next
;
525 MyFreePool(Previous
);
527 } // VOID DeleteStringList()