1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "string2.h" |
3 | #include <linux/kernel.h> |
4 | #include <linux/string.h> |
5 | #include <stdlib.h> |
6 | |
7 | #include <linux/ctype.h> |
8 | |
9 | const char *graph_dotted_line = |
10 | "---------------------------------------------------------------------" |
11 | "---------------------------------------------------------------------" |
12 | "---------------------------------------------------------------------" ; |
13 | const char *dots = |
14 | "....................................................................." |
15 | "....................................................................." |
16 | "....................................................................." ; |
17 | |
18 | /* |
19 | * perf_atoll() |
20 | * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") |
21 | * and return its numeric value |
22 | */ |
23 | s64 perf_atoll(const char *str) |
24 | { |
25 | s64 length; |
26 | char *p; |
27 | char c; |
28 | |
29 | if (!isdigit(c: str[0])) |
30 | goto out_err; |
31 | |
32 | length = strtoll(str, &p, 10); |
33 | switch (c = *p++) { |
34 | case 'b': case 'B': |
35 | if (*p) |
36 | goto out_err; |
37 | |
38 | fallthrough; |
39 | case '\0': |
40 | return length; |
41 | default: |
42 | goto out_err; |
43 | /* two-letter suffices */ |
44 | case 'k': case 'K': |
45 | length <<= 10; |
46 | break; |
47 | case 'm': case 'M': |
48 | length <<= 20; |
49 | break; |
50 | case 'g': case 'G': |
51 | length <<= 30; |
52 | break; |
53 | case 't': case 'T': |
54 | length <<= 40; |
55 | break; |
56 | } |
57 | /* we want the cases to match */ |
58 | if (islower(c)) { |
59 | if (strcmp(p, "b" ) != 0) |
60 | goto out_err; |
61 | } else { |
62 | if (strcmp(p, "B" ) != 0) |
63 | goto out_err; |
64 | } |
65 | return length; |
66 | |
67 | out_err: |
68 | return -1; |
69 | } |
70 | |
71 | /* Character class matching */ |
72 | static bool __match_charclass(const char *pat, char c, const char **npat) |
73 | { |
74 | bool complement = false, ret = true; |
75 | |
76 | if (*pat == '!') { |
77 | complement = true; |
78 | pat++; |
79 | } |
80 | if (*pat++ == c) /* First character is special */ |
81 | goto end; |
82 | |
83 | while (*pat && *pat != ']') { /* Matching */ |
84 | if (*pat == '-' && *(pat + 1) != ']') { /* Range */ |
85 | if (*(pat - 1) <= c && c <= *(pat + 1)) |
86 | goto end; |
87 | if (*(pat - 1) > *(pat + 1)) |
88 | goto error; |
89 | pat += 2; |
90 | } else if (*pat++ == c) |
91 | goto end; |
92 | } |
93 | if (!*pat) |
94 | goto error; |
95 | ret = false; |
96 | |
97 | end: |
98 | while (*pat && *pat != ']') /* Searching closing */ |
99 | pat++; |
100 | if (!*pat) |
101 | goto error; |
102 | *npat = pat + 1; |
103 | return complement ? !ret : ret; |
104 | |
105 | error: |
106 | return false; |
107 | } |
108 | |
109 | /* Glob/lazy pattern matching */ |
110 | static bool __match_glob(const char *str, const char *pat, bool ignore_space, |
111 | bool case_ins) |
112 | { |
113 | while (*str && *pat && *pat != '*') { |
114 | if (ignore_space) { |
115 | /* Ignore spaces for lazy matching */ |
116 | if (isspace(*str)) { |
117 | str++; |
118 | continue; |
119 | } |
120 | if (isspace(*pat)) { |
121 | pat++; |
122 | continue; |
123 | } |
124 | } |
125 | if (*pat == '?') { /* Matches any single character */ |
126 | str++; |
127 | pat++; |
128 | continue; |
129 | } else if (*pat == '[') /* Character classes/Ranges */ |
130 | if (__match_charclass(pat: pat + 1, c: *str, npat: &pat)) { |
131 | str++; |
132 | continue; |
133 | } else |
134 | return false; |
135 | else if (*pat == '\\') /* Escaped char match as normal char */ |
136 | pat++; |
137 | if (case_ins) { |
138 | if (tolower(*str) != tolower(*pat)) |
139 | return false; |
140 | } else if (*str != *pat) |
141 | return false; |
142 | str++; |
143 | pat++; |
144 | } |
145 | /* Check wild card */ |
146 | if (*pat == '*') { |
147 | while (*pat == '*') |
148 | pat++; |
149 | if (!*pat) /* Tail wild card matches all */ |
150 | return true; |
151 | while (*str) |
152 | if (__match_glob(str: str++, pat, ignore_space, case_ins)) |
153 | return true; |
154 | } |
155 | return !*str && !*pat; |
156 | } |
157 | |
158 | /** |
159 | * strglobmatch - glob expression pattern matching |
160 | * @str: the target string to match |
161 | * @pat: the pattern string to match |
162 | * |
163 | * This returns true if the @str matches @pat. @pat can includes wildcards |
164 | * ('*','?') and character classes ([CHARS], complementation and ranges are |
165 | * also supported). Also, this supports escape character ('\') to use special |
166 | * characters as normal character. |
167 | * |
168 | * Note: if @pat syntax is broken, this always returns false. |
169 | */ |
170 | bool strglobmatch(const char *str, const char *pat) |
171 | { |
172 | return __match_glob(str, pat, ignore_space: false, case_ins: false); |
173 | } |
174 | |
175 | bool strglobmatch_nocase(const char *str, const char *pat) |
176 | { |
177 | return __match_glob(str, pat, ignore_space: false, case_ins: true); |
178 | } |
179 | |
180 | /** |
181 | * strlazymatch - matching pattern strings lazily with glob pattern |
182 | * @str: the target string to match |
183 | * @pat: the pattern string to match |
184 | * |
185 | * This is similar to strglobmatch, except this ignores spaces in |
186 | * the target string. |
187 | */ |
188 | bool strlazymatch(const char *str, const char *pat) |
189 | { |
190 | return __match_glob(str, pat, ignore_space: true, case_ins: false); |
191 | } |
192 | |
193 | /** |
194 | * strtailcmp - Compare the tail of two strings |
195 | * @s1: 1st string to be compared |
196 | * @s2: 2nd string to be compared |
197 | * |
198 | * Return 0 if whole of either string is same as another's tail part. |
199 | */ |
200 | int strtailcmp(const char *s1, const char *s2) |
201 | { |
202 | int i1 = strlen(s1); |
203 | int i2 = strlen(s2); |
204 | while (--i1 >= 0 && --i2 >= 0) { |
205 | if (s1[i1] != s2[i2]) |
206 | return s1[i1] - s2[i2]; |
207 | } |
208 | return 0; |
209 | } |
210 | |
211 | char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints) |
212 | { |
213 | /* |
214 | * FIXME: replace this with an expression using log10() when we |
215 | * find a suitable implementation, maybe the one in the dvb drivers... |
216 | * |
217 | * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators |
218 | */ |
219 | size_t size = nints * 28 + 1; /* \0 */ |
220 | size_t i, printed = 0; |
221 | char *expr = malloc(size); |
222 | |
223 | if (expr) { |
224 | const char *or_and = "||" , *eq_neq = "==" ; |
225 | char *e = expr; |
226 | |
227 | if (!in) { |
228 | or_and = "&&" ; |
229 | eq_neq = "!=" ; |
230 | } |
231 | |
232 | for (i = 0; i < nints; ++i) { |
233 | if (printed == size) |
234 | goto out_err_overflow; |
235 | |
236 | if (i > 0) |
237 | printed += scnprintf(buf: e + printed, size: size - printed, fmt: " %s " , or_and); |
238 | printed += scnprintf(buf: e + printed, size: size - printed, |
239 | fmt: "%s %s %d" , var, eq_neq, ints[i]); |
240 | } |
241 | } |
242 | |
243 | return expr; |
244 | |
245 | out_err_overflow: |
246 | free(expr); |
247 | return NULL; |
248 | } |
249 | |
250 | /* Like strpbrk(), but not break if it is right after a backslash (escaped) */ |
251 | char *strpbrk_esc(char *str, const char *stopset) |
252 | { |
253 | char *ptr; |
254 | |
255 | do { |
256 | ptr = strpbrk(str, stopset); |
257 | if (ptr == str || |
258 | (ptr == str + 1 && *(ptr - 1) != '\\')) |
259 | break; |
260 | str = ptr + 1; |
261 | } while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\'); |
262 | |
263 | return ptr; |
264 | } |
265 | |
266 | /* Like strdup, but do not copy a single backslash */ |
267 | char *strdup_esc(const char *str) |
268 | { |
269 | char *s, *d, *p, *ret = strdup(str); |
270 | |
271 | if (!ret) |
272 | return NULL; |
273 | |
274 | d = strchr(ret, '\\'); |
275 | if (!d) |
276 | return ret; |
277 | |
278 | s = d + 1; |
279 | do { |
280 | if (*s == '\0') { |
281 | *d = '\0'; |
282 | break; |
283 | } |
284 | p = strchr(s + 1, '\\'); |
285 | if (p) { |
286 | memmove(d, s, p - s); |
287 | d += p - s; |
288 | s = p + 1; |
289 | } else |
290 | memmove(d, s, strlen(s) + 1); |
291 | } while (p); |
292 | |
293 | return ret; |
294 | } |
295 | |
296 | unsigned int hex(char c) |
297 | { |
298 | if (c >= '0' && c <= '9') |
299 | return c - '0'; |
300 | if (c >= 'a' && c <= 'f') |
301 | return c - 'a' + 10; |
302 | return c - 'A' + 10; |
303 | } |
304 | |
305 | /* |
306 | * Replace all occurrences of character 'needle' in string 'haystack' with |
307 | * string 'replace' |
308 | * |
309 | * The new string could be longer so a new string is returned which must be |
310 | * freed. |
311 | */ |
312 | char *strreplace_chars(char needle, const char *haystack, const char *replace) |
313 | { |
314 | int replace_len = strlen(replace); |
315 | char *new_s, *to; |
316 | const char *loc = strchr(haystack, needle); |
317 | const char *from = haystack; |
318 | int num = 0; |
319 | |
320 | /* Count occurrences */ |
321 | while (loc) { |
322 | loc = strchr(loc + 1, needle); |
323 | num++; |
324 | } |
325 | |
326 | /* Allocate enough space for replacements and reset first location */ |
327 | new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1)); |
328 | if (!new_s) |
329 | return NULL; |
330 | loc = strchr(haystack, needle); |
331 | to = new_s; |
332 | |
333 | while (loc) { |
334 | /* Copy original string up to found char and update positions */ |
335 | memcpy(to, from, 1 + loc - from); |
336 | to += loc - from; |
337 | from = loc + 1; |
338 | |
339 | /* Copy replacement string and update positions */ |
340 | memcpy(to, replace, replace_len); |
341 | to += replace_len; |
342 | |
343 | /* needle next occurrence or end of string */ |
344 | loc = strchr(from, needle); |
345 | } |
346 | |
347 | /* Copy any remaining chars + null */ |
348 | strcpy(p: to, q: from); |
349 | |
350 | return new_s; |
351 | } |
352 | |