]>
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 // Returns the number of characters that are in common between
271 // String1 and String2 before they diverge. For instance, if
272 // String1 is "FooBar" and String2 is "FoodiesBar", this function
273 // will return "3", since they both start with "Foo".
274 UINTN
NumCharsInCommon(IN CHAR16
* String1
, IN CHAR16
* String2
) {
276 if ((String1
== NULL
) || (String2
== NULL
))
278 while ((String1
[Count
] != L
'\0') && (String2
[Count
] != L
'\0') && (String1
[Count
] == String2
[Count
]))
281 } // UINTN NumCharsInCommon()
283 // Find the #Index element (numbered from 0) in a comma-delimited string
285 // Returns the found element, or NULL if Index is out of range or InString
286 // is NULL. Note that the calling function is responsible for freeing the
287 // memory associated with the returned string pointer.
288 CHAR16
*FindCommaDelimited(IN CHAR16
*InString
, IN UINTN Index
) {
289 UINTN StartPos
= 0, CurPos
= 0, InLength
;
290 BOOLEAN Found
= FALSE
;
291 CHAR16
*FoundString
= NULL
;
293 if (InString
!= NULL
) {
294 InLength
= StrLen(InString
);
295 // After while() loop, StartPos marks start of item #Index
296 while ((Index
> 0) && (CurPos
< InLength
)) {
297 if (InString
[CurPos
] == L
',') {
299 StartPos
= CurPos
+ 1;
303 // After while() loop, CurPos is one past the end of the element
304 while ((CurPos
< InLength
) && (!Found
)) {
305 if (InString
[CurPos
] == L
',')
311 FoundString
= StrDuplicate(&InString
[StartPos
]);
312 if (FoundString
!= NULL
)
313 FoundString
[CurPos
- StartPos
] = 0;
315 return (FoundString
);
316 } // CHAR16 *FindCommaDelimited()
318 // Returns TRUE if SmallString is an element in the comma-delimited List,
319 // FALSE otherwise. Performs comparison case-insensitively.
320 BOOLEAN
IsIn(IN CHAR16
*SmallString
, IN CHAR16
*List
) {
322 BOOLEAN Found
= FALSE
;
325 if (SmallString
&& List
) {
326 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
327 if (MyStriCmp(OneElement
, SmallString
))
334 // Returns TRUE if any element of List can be found as a substring of
335 // BigString, FALSE otherwise. Performs comparisons case-insensitively.
336 BOOLEAN
IsInSubstring(IN CHAR16
*BigString
, IN CHAR16
*List
) {
337 UINTN i
= 0, ElementLength
;
338 BOOLEAN Found
= FALSE
;
341 if (BigString
&& List
) {
342 while (!Found
&& (OneElement
= FindCommaDelimited(List
, i
++))) {
343 ElementLength
= StrLen(OneElement
);
344 if ((ElementLength
<= StrLen(BigString
)) && (StriSubCmp(OneElement
, BigString
)))
349 } // BOOLEAN IsSubstringIn()
351 // Replace *SearchString in **MainString with *ReplString -- but if *SearchString
352 // is preceded by "%", instead remove that character.
353 // Returns TRUE if replacement was done, FALSE otherwise.
354 BOOLEAN
ReplaceSubstring(IN OUT CHAR16
**MainString
, IN CHAR16
*SearchString
, IN CHAR16
*ReplString
) {
355 BOOLEAN WasReplaced
= FALSE
;
356 CHAR16
*FoundSearchString
, *NewString
, *EndString
;
358 FoundSearchString
= MyStrStr(*MainString
, SearchString
);
359 if (FoundSearchString
) {
360 NewString
= AllocateZeroPool(sizeof(CHAR16
) * StrLen(*MainString
));
362 EndString
= &(FoundSearchString
[StrLen(SearchString
)]);
363 FoundSearchString
[0] = L
'\0';
364 if ((FoundSearchString
> *MainString
) && (FoundSearchString
[-1] == L
'%')) {
365 FoundSearchString
[-1] = L
'\0';
366 ReplString
= SearchString
;
368 StrCpy(NewString
, *MainString
);
369 MergeStrings(&NewString
, ReplString
, L
'\0');
370 MergeStrings(&NewString
, EndString
, L
'\0');
371 MyFreePool(MainString
);
372 *MainString
= NewString
;
377 } // BOOLEAN ReplaceSubstring()
379 // Returns TRUE if *Input contains nothing but valid hexadecimal characters,
380 // FALSE otherwise. Note that a leading "0x" is NOT acceptable in the input!
381 BOOLEAN
IsValidHex(CHAR16
*Input
) {
382 BOOLEAN IsHex
= TRUE
;
385 while ((Input
[i
] != L
'\0') && IsHex
) {
386 if (!(((Input
[i
] >= L
'0') && (Input
[i
] <= L
'9')) ||
387 ((Input
[i
] >= L
'A') && (Input
[i
] <= L
'F')) ||
388 ((Input
[i
] >= L
'a') && (Input
[i
] <= L
'f')))) {
394 } // BOOLEAN IsValidHex()
396 // Converts consecutive characters in the input string into a
397 // number, interpreting the string as a hexadecimal number, starting
398 // at the specified position and continuing for the specified number
399 // of characters or until the end of the string, whichever is first.
400 // NumChars must be between 1 and 16. Ignores invalid characters.
401 UINT64
StrToHex(CHAR16
*Input
, UINTN Pos
, UINTN NumChars
) {
402 UINT64 retval
= 0x00;
403 UINTN NumDone
= 0, InputLength
;
406 if ((Input
== NULL
) || (NumChars
== 0) || (NumChars
> 16)) {
410 InputLength
= StrLen(Input
);
411 while ((Pos
<= InputLength
) && (NumDone
< NumChars
)) {
413 if ((a
>= '0') && (a
<= '9')) {
418 if ((a
>= 'a') && (a
<= 'f')) {
420 retval
+= (a
- 'a' + 0x0a);
423 if ((a
>= 'A') && (a
<= 'F')) {
425 retval
+= (a
- 'A' + 0x0a);
433 // Returns TRUE if UnknownString can be interpreted as a GUID, FALSE otherwise.
434 // Note that the input string must have no extraneous spaces and must be
435 // conventionally formatted as a 36-character GUID, complete with dashes in
436 // appropriate places.
437 BOOLEAN
IsGuid(CHAR16
*UnknownString
) {
439 BOOLEAN retval
= TRUE
;
442 if (UnknownString
== NULL
)
445 Length
= StrLen(UnknownString
);
449 for (i
= 0; i
< Length
; i
++) {
450 a
= UnknownString
[i
];
451 if ((i
== 8) || (i
== 13) || (i
== 18) || (i
== 23)) {
454 } else if (((a
< L
'a') || (a
> L
'f')) &&
455 ((a
< L
'A') || (a
> L
'F')) &&
456 ((a
< L
'0') && (a
> L
'9'))) {
461 } // BOOLEAN IsGuid()
463 // Return the GUID as a string, suitable for display to the user. Note that the calling
464 // function is responsible for freeing the allocated memory.
465 CHAR16
* GuidAsString(EFI_GUID
*GuidData
) {
468 TheString
= AllocateZeroPool(42 * sizeof(CHAR16
));
469 if (TheString
!= 0) {
470 SPrint (TheString
, 82, L
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
471 (UINTN
)GuidData
->Data1
, (UINTN
)GuidData
->Data2
, (UINTN
)GuidData
->Data3
,
472 (UINTN
)GuidData
->Data4
[0], (UINTN
)GuidData
->Data4
[1], (UINTN
)GuidData
->Data4
[2],
473 (UINTN
)GuidData
->Data4
[3], (UINTN
)GuidData
->Data4
[4], (UINTN
)GuidData
->Data4
[5],
474 (UINTN
)GuidData
->Data4
[6], (UINTN
)GuidData
->Data4
[7]);
477 } // GuidAsString(EFI_GUID *GuidData)
479 EFI_GUID
StringAsGuid(CHAR16
* InString
) {
480 EFI_GUID Guid
= NULL_GUID_VALUE
;
482 if (!IsGuid(InString
)) {
486 Guid
.Data1
= (UINT32
) StrToHex(InString
, 0, 8);
487 Guid
.Data2
= (UINT16
) StrToHex(InString
, 9, 4);
488 Guid
.Data3
= (UINT16
) StrToHex(InString
, 14, 4);
489 Guid
.Data4
[0] = (UINT8
) StrToHex(InString
, 19, 2);
490 Guid
.Data4
[1] = (UINT8
) StrToHex(InString
, 21, 2);
491 Guid
.Data4
[2] = (UINT8
) StrToHex(InString
, 23, 2);
492 Guid
.Data4
[3] = (UINT8
) StrToHex(InString
, 26, 2);
493 Guid
.Data4
[4] = (UINT8
) StrToHex(InString
, 28, 2);
494 Guid
.Data4
[5] = (UINT8
) StrToHex(InString
, 30, 2);
495 Guid
.Data4
[6] = (UINT8
) StrToHex(InString
, 32, 2);
496 Guid
.Data4
[7] = (UINT8
) StrToHex(InString
, 34, 2);
499 } // EFI_GUID StringAsGuid()
501 // Delete the STRING_LIST pointed to by *StringList.
502 VOID
DeleteStringList(STRING_LIST
*StringList
) {
503 STRING_LIST
*Current
= StringList
, *Previous
;
505 while (Current
!= NULL
) {
506 MyFreePool(Current
->Value
);
508 Current
= Current
->Next
;
509 MyFreePool(Previous
);
511 } // VOID DeleteStringList()