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  */
perf_atoll(const char * str)23 s64 perf_atoll(const char *str)
24 {
25 	s64 length;
26 	char *p;
27 	char c;
28 
29 	if (!isdigit(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 */
__match_charclass(const char * pat,char c,const char ** npat)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 */
__match_glob(const char * str,const char * pat,bool ignore_space,bool case_ins)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 + 1, *str, &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++, 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  */
strglobmatch(const char * str,const char * pat)170 bool strglobmatch(const char *str, const char *pat)
171 {
172 	return __match_glob(str, pat, false, false);
173 }
174 
strglobmatch_nocase(const char * str,const char * pat)175 bool strglobmatch_nocase(const char *str, const char *pat)
176 {
177 	return __match_glob(str, pat, false, 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  */
strlazymatch(const char * str,const char * pat)188 bool strlazymatch(const char *str, const char *pat)
189 {
190 	return __match_glob(str, pat, true, 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  */
strtailcmp(const char * s1,const char * s2)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 
asprintf_expr_inout_ints(const char * var,bool in,size_t nints,int * ints)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(e + printed, size - printed, " %s ", or_and);
238 			printed += scnprintf(e + printed, size - printed,
239 					     "%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) */
strpbrk_esc(char * str,const char * stopset)251 char *strpbrk_esc(char *str, const char *stopset)
252 {
253 	char *ptr;
254 
255 	do {
256 		ptr = strpbrk(str, stopset);
257 		if (!ptr) {
258 			/* stopset not in str. */
259 			break;
260 		}
261 		if (ptr == str) {
262 			/* stopset character is first in str. */
263 			break;
264 		}
265 		if (ptr == str + 1 && str[0] != '\\') {
266 			/* stopset chacter is second and wasn't preceded by a '\'. */
267 			break;
268 		}
269 		str = ptr + 1;
270 	} while (ptr[-1] == '\\' && ptr[-2] != '\\');
271 
272 	return ptr;
273 }
274 
275 /* Like strpbrk_esc(), but not break if it is quoted with single/double quotes */
strpbrk_esq(char * str,const char * stopset)276 char *strpbrk_esq(char *str, const char *stopset)
277 {
278 	char *_stopset = NULL;
279 	char *ptr;
280 	const char *squote = "'";
281 	const char *dquote = "\"";
282 
283 	if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0)
284 		return NULL;
285 
286 	do {
287 		ptr = strpbrk_esc(str, _stopset);
288 		if (!ptr)
289 			break;
290 		if (*ptr == *squote)
291 			ptr = strpbrk_esc(ptr + 1, squote);
292 		else if (*ptr == *dquote)
293 			ptr = strpbrk_esc(ptr + 1, dquote);
294 		else
295 			break;
296 		str = ptr + 1;
297 	} while (ptr);
298 
299 	free(_stopset);
300 	return ptr;
301 }
302 
303 /* Like strdup, but do not copy a single backslash */
strdup_esc(const char * str)304 char *strdup_esc(const char *str)
305 {
306 	char *s, *d, *p, *ret = strdup(str);
307 
308 	if (!ret)
309 		return NULL;
310 
311 	d = strchr(ret, '\\');
312 	if (!d)
313 		return ret;
314 
315 	s = d + 1;
316 	do {
317 		if (*s == '\0') {
318 			*d = '\0';
319 			break;
320 		}
321 		p = strchr(s + 1, '\\');
322 		if (p) {
323 			memmove(d, s, p - s);
324 			d += p - s;
325 			s = p + 1;
326 		} else
327 			memmove(d, s, strlen(s) + 1);
328 	} while (p);
329 
330 	return ret;
331 }
332 
333 /* Remove backslash right before quote and return next quote address. */
remove_consumed_esc(char * str,int len,int quote)334 static char *remove_consumed_esc(char *str, int len, int quote)
335 {
336 	char *ptr = str, *end = str + len;
337 
338 	while (*ptr != quote && ptr < end) {
339 		if (*ptr == '\\' && *(ptr + 1) == quote) {
340 			memmove(ptr, ptr + 1, end - (ptr + 1));
341 			/* now *ptr is `quote`. */
342 			end--;
343 		}
344 		ptr++;
345 	}
346 
347 	return *ptr == quote ? ptr : NULL;
348 }
349 
350 /*
351  * Like strdup_esc, but keep quoted string as it is (and single backslash
352  * before quote is removed). If there is no closed quote, return NULL.
353  */
strdup_esq(const char * str)354 char *strdup_esq(const char *str)
355 {
356 	char *d, *ret;
357 
358 	/* If there is no quote, return normal strdup_esc() */
359 	d = strpbrk_esc((char *)str, "\"'");
360 	if (!d)
361 		return strdup_esc(str);
362 
363 	ret = strdup(str);
364 	if (!ret)
365 		return NULL;
366 
367 	d = ret;
368 	do {
369 		d = strpbrk(d, "\\\"\'");
370 		if (!d)
371 			break;
372 
373 		if (*d == '"' || *d == '\'') {
374 			/* This is non-escaped quote */
375 			int quote = *d;
376 			int len = strlen(d + 1) + 1;
377 
378 			/*
379 			 * Remove the start quote and remove consumed escape (backslash
380 			 * before quote) and remove the end quote. If there is no end
381 			 * quote, it is the input error.
382 			 */
383 			memmove(d, d + 1, len);
384 			d = remove_consumed_esc(d, len, quote);
385 			if (!d)
386 				goto error;
387 			memmove(d, d + 1, strlen(d + 1) + 1);
388 		}
389 		if (*d == '\\') {
390 			memmove(d, d + 1, strlen(d + 1) + 1);
391 			if (*d == '\\') {
392 				/* double backslash -- keep the second one. */
393 				d++;
394 			}
395 		}
396 	} while (*d != '\0');
397 
398 	return ret;
399 
400 error:
401 	free(ret);
402 	return NULL;
403 }
404 
hex(char c)405 unsigned int hex(char c)
406 {
407 	if (c >= '0' && c <= '9')
408 		return c - '0';
409 	if (c >= 'a' && c <= 'f')
410 		return c - 'a' + 10;
411 	return c - 'A' + 10;
412 }
413 
414 /*
415  * Replace all occurrences of character 'needle' in string 'haystack' with
416  * string 'replace'
417  *
418  * The new string could be longer so a new string is returned which must be
419  * freed.
420  */
strreplace_chars(char needle,const char * haystack,const char * replace)421 char *strreplace_chars(char needle, const char *haystack, const char *replace)
422 {
423 	int replace_len = strlen(replace);
424 	char *new_s, *to;
425 	const char *loc = strchr(haystack, needle);
426 	const char *from = haystack;
427 	int num = 0;
428 
429 	/* Count occurrences */
430 	while (loc) {
431 		loc = strchr(loc + 1, needle);
432 		num++;
433 	}
434 
435 	/* Allocate enough space for replacements and reset first location */
436 	new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1));
437 	if (!new_s)
438 		return NULL;
439 	loc = strchr(haystack, needle);
440 	to = new_s;
441 
442 	while (loc) {
443 		/* Copy original string up to found char and update positions */
444 		memcpy(to, from, 1 + loc - from);
445 		to += loc - from;
446 		from = loc + 1;
447 
448 		/* Copy replacement string and update positions */
449 		memcpy(to, replace, replace_len);
450 		to += replace_len;
451 
452 		/* needle next occurrence or end of string */
453 		loc = strchr(from, needle);
454 	}
455 
456 	/* Copy any remaining chars + null */
457 	strcpy(to, from);
458 
459 	return new_s;
460 }
461