Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <awesomekling@gmail.com>
3 * Copyright (c) 2020, Fei Wu <f.eiwu@yahoo.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <AK/String.h>
29#include <AK/StringUtils.h>
30#include <AK/StringView.h>
31
32namespace AK {
33
34namespace StringUtils {
35
36bool matches(const StringView& str, const StringView& mask, CaseSensitivity case_sensitivity)
37{
38 if (str.is_null() || mask.is_null())
39 return str.is_null() && mask.is_null();
40
41 if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
42 const String str_lower = String(str).to_lowercase();
43 const String mask_lower = String(mask).to_lowercase();
44 return matches(str_lower, mask_lower, CaseSensitivity::CaseSensitive);
45 }
46
47 const char* string_ptr = str.characters_without_null_termination();
48 const char* string_end = string_ptr + str.length();
49 const char* mask_ptr = mask.characters_without_null_termination();
50 const char* mask_end = mask_ptr + mask.length();
51
52 // Match string against mask directly unless we hit a *
53 while ((string_ptr < string_end) && (mask_ptr < mask_end) && (*mask_ptr != '*')) {
54 if ((*mask_ptr != *string_ptr) && (*mask_ptr != '?'))
55 return false;
56 mask_ptr++;
57 string_ptr++;
58 }
59
60 const char* cp = nullptr;
61 const char* mp = nullptr;
62
63 while (string_ptr < string_end) {
64 if ((mask_ptr < mask_end) && (*mask_ptr == '*')) {
65 // If we have only a * left, there is no way to not match.
66 if (++mask_ptr == mask_end)
67 return true;
68 mp = mask_ptr;
69 cp = string_ptr + 1;
70 } else if ((mask_ptr < mask_end) && ((*mask_ptr == *string_ptr) || (*mask_ptr == '?'))) {
71 mask_ptr++;
72 string_ptr++;
73 } else if ((cp != nullptr) && (mp != nullptr)) {
74 mask_ptr = mp;
75 string_ptr = cp++;
76 } else {
77 break;
78 }
79 }
80
81 // Handle any trailing mask
82 while ((mask_ptr < mask_end) && (*mask_ptr == '*'))
83 mask_ptr++;
84
85 // If we 'ate' all of the mask and the string then we match.
86 return (mask_ptr == mask_end) && string_ptr == string_end;
87}
88
89int convert_to_int(const StringView& str, bool& ok)
90{
91 if (str.is_empty()) {
92 ok = false;
93 return 0;
94 }
95
96 bool negative = false;
97 size_t i = 0;
98 const auto characters = str.characters_without_null_termination();
99
100 if (characters[0] == '-' || characters[0] == '+') {
101 if (str.length() == 1) {
102 ok = false;
103 return 0;
104 }
105 i++;
106 negative = (characters[0] == '-');
107 }
108
109 int value = 0;
110 for (; i < str.length(); i++) {
111 if (characters[i] < '0' || characters[i] > '9') {
112 ok = false;
113 return 0;
114 }
115 value = value * 10;
116 value += characters[i] - '0';
117 }
118 ok = true;
119
120 return negative ? -value : value;
121}
122
123unsigned convert_to_uint(const StringView& str, bool& ok)
124{
125 if (str.is_empty()) {
126 ok = false;
127 return 0;
128 }
129
130 unsigned value = 0;
131 const auto characters = str.characters_without_null_termination();
132
133 for (size_t i = 0; i < str.length(); i++) {
134 if (characters[i] < '0' || characters[i] > '9') {
135 ok = false;
136 return 0;
137 }
138 value = value * 10;
139 value += characters[i] - '0';
140 }
141 ok = true;
142
143 return value;
144}
145
146static inline char to_lowercase(char c)
147{
148 if (c >= 'A' && c <= 'Z')
149 return c | 0x20;
150 return c;
151}
152
153bool equals_ignoring_case(const StringView& a, const StringView& b)
154{
155 if (a.impl() && a.impl() == b.impl())
156 return true;
157 if (a.length() != b.length())
158 return false;
159 for (size_t i = 0; i < a.length(); ++i) {
160 if (to_lowercase(a.characters_without_null_termination()[i]) != to_lowercase(b.characters_without_null_termination()[i]))
161 return false;
162 }
163 return true;
164}
165
166}
167
168}