xref: /linux-6.15/tools/perf/util/string.c (revision 6a9fa4e3)
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 "sane_ctype.h"
8 
9 const char *graph_dotted_line =
10 	"---------------------------------------------------------------------"
11 	"---------------------------------------------------------------------"
12 	"---------------------------------------------------------------------";
13 const char *dots =
14 	"....................................................................."
15 	"....................................................................."
16 	".....................................................................";
17 
18 #define K 1024LL
19 /*
20  * perf_atoll()
21  * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
22  * and return its numeric value
23  */
24 s64 perf_atoll(const char *str)
25 {
26 	s64 length;
27 	char *p;
28 	char c;
29 
30 	if (!isdigit(str[0]))
31 		goto out_err;
32 
33 	length = strtoll(str, &p, 10);
34 	switch (c = *p++) {
35 		case 'b': case 'B':
36 			if (*p)
37 				goto out_err;
38 
39 			__fallthrough;
40 		case '\0':
41 			return length;
42 		default:
43 			goto out_err;
44 		/* two-letter suffices */
45 		case 'k': case 'K':
46 			length <<= 10;
47 			break;
48 		case 'm': case 'M':
49 			length <<= 20;
50 			break;
51 		case 'g': case 'G':
52 			length <<= 30;
53 			break;
54 		case 't': case 'T':
55 			length <<= 40;
56 			break;
57 	}
58 	/* we want the cases to match */
59 	if (islower(c)) {
60 		if (strcmp(p, "b") != 0)
61 			goto out_err;
62 	} else {
63 		if (strcmp(p, "B") != 0)
64 			goto out_err;
65 	}
66 	return length;
67 
68 out_err:
69 	return -1;
70 }
71 
72 /*
73  * Helper function for splitting a string into an argv-like array.
74  * originally copied from lib/argv_split.c
75  */
76 static const char *skip_sep(const char *cp)
77 {
78 	while (*cp && isspace(*cp))
79 		cp++;
80 
81 	return cp;
82 }
83 
84 static const char *skip_arg(const char *cp)
85 {
86 	while (*cp && !isspace(*cp))
87 		cp++;
88 
89 	return cp;
90 }
91 
92 static int count_argc(const char *str)
93 {
94 	int count = 0;
95 
96 	while (*str) {
97 		str = skip_sep(str);
98 		if (*str) {
99 			count++;
100 			str = skip_arg(str);
101 		}
102 	}
103 
104 	return count;
105 }
106 
107 /**
108  * argv_free - free an argv
109  * @argv - the argument vector to be freed
110  *
111  * Frees an argv and the strings it points to.
112  */
113 void argv_free(char **argv)
114 {
115 	char **p;
116 	for (p = argv; *p; p++) {
117 		free(*p);
118 		*p = NULL;
119 	}
120 
121 	free(argv);
122 }
123 
124 /**
125  * argv_split - split a string at whitespace, returning an argv
126  * @str: the string to be split
127  * @argcp: returned argument count
128  *
129  * Returns an array of pointers to strings which are split out from
130  * @str.  This is performed by strictly splitting on white-space; no
131  * quote processing is performed.  Multiple whitespace characters are
132  * considered to be a single argument separator.  The returned array
133  * is always NULL-terminated.  Returns NULL on memory allocation
134  * failure.
135  */
136 char **argv_split(const char *str, int *argcp)
137 {
138 	int argc = count_argc(str);
139 	char **argv = calloc(argc + 1, sizeof(*argv));
140 	char **argvp;
141 
142 	if (argv == NULL)
143 		goto out;
144 
145 	if (argcp)
146 		*argcp = argc;
147 
148 	argvp = argv;
149 
150 	while (*str) {
151 		str = skip_sep(str);
152 
153 		if (*str) {
154 			const char *p = str;
155 			char *t;
156 
157 			str = skip_arg(str);
158 
159 			t = strndup(p, str-p);
160 			if (t == NULL)
161 				goto fail;
162 			*argvp++ = t;
163 		}
164 	}
165 	*argvp = NULL;
166 
167 out:
168 	return argv;
169 
170 fail:
171 	argv_free(argv);
172 	return NULL;
173 }
174 
175 /* Character class matching */
176 static bool __match_charclass(const char *pat, char c, const char **npat)
177 {
178 	bool complement = false, ret = true;
179 
180 	if (*pat == '!') {
181 		complement = true;
182 		pat++;
183 	}
184 	if (*pat++ == c)	/* First character is special */
185 		goto end;
186 
187 	while (*pat && *pat != ']') {	/* Matching */
188 		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
189 			if (*(pat - 1) <= c && c <= *(pat + 1))
190 				goto end;
191 			if (*(pat - 1) > *(pat + 1))
192 				goto error;
193 			pat += 2;
194 		} else if (*pat++ == c)
195 			goto end;
196 	}
197 	if (!*pat)
198 		goto error;
199 	ret = false;
200 
201 end:
202 	while (*pat && *pat != ']')	/* Searching closing */
203 		pat++;
204 	if (!*pat)
205 		goto error;
206 	*npat = pat + 1;
207 	return complement ? !ret : ret;
208 
209 error:
210 	return false;
211 }
212 
213 /* Glob/lazy pattern matching */
214 static bool __match_glob(const char *str, const char *pat, bool ignore_space,
215 			bool case_ins)
216 {
217 	while (*str && *pat && *pat != '*') {
218 		if (ignore_space) {
219 			/* Ignore spaces for lazy matching */
220 			if (isspace(*str)) {
221 				str++;
222 				continue;
223 			}
224 			if (isspace(*pat)) {
225 				pat++;
226 				continue;
227 			}
228 		}
229 		if (*pat == '?') {	/* Matches any single character */
230 			str++;
231 			pat++;
232 			continue;
233 		} else if (*pat == '[')	/* Character classes/Ranges */
234 			if (__match_charclass(pat + 1, *str, &pat)) {
235 				str++;
236 				continue;
237 			} else
238 				return false;
239 		else if (*pat == '\\') /* Escaped char match as normal char */
240 			pat++;
241 		if (case_ins) {
242 			if (tolower(*str) != tolower(*pat))
243 				return false;
244 		} else if (*str != *pat)
245 			return false;
246 		str++;
247 		pat++;
248 	}
249 	/* Check wild card */
250 	if (*pat == '*') {
251 		while (*pat == '*')
252 			pat++;
253 		if (!*pat)	/* Tail wild card matches all */
254 			return true;
255 		while (*str)
256 			if (__match_glob(str++, pat, ignore_space, case_ins))
257 				return true;
258 	}
259 	return !*str && !*pat;
260 }
261 
262 /**
263  * strglobmatch - glob expression pattern matching
264  * @str: the target string to match
265  * @pat: the pattern string to match
266  *
267  * This returns true if the @str matches @pat. @pat can includes wildcards
268  * ('*','?') and character classes ([CHARS], complementation and ranges are
269  * also supported). Also, this supports escape character ('\') to use special
270  * characters as normal character.
271  *
272  * Note: if @pat syntax is broken, this always returns false.
273  */
274 bool strglobmatch(const char *str, const char *pat)
275 {
276 	return __match_glob(str, pat, false, false);
277 }
278 
279 bool strglobmatch_nocase(const char *str, const char *pat)
280 {
281 	return __match_glob(str, pat, false, true);
282 }
283 
284 /**
285  * strlazymatch - matching pattern strings lazily with glob pattern
286  * @str: the target string to match
287  * @pat: the pattern string to match
288  *
289  * This is similar to strglobmatch, except this ignores spaces in
290  * the target string.
291  */
292 bool strlazymatch(const char *str, const char *pat)
293 {
294 	return __match_glob(str, pat, true, false);
295 }
296 
297 /**
298  * strtailcmp - Compare the tail of two strings
299  * @s1: 1st string to be compared
300  * @s2: 2nd string to be compared
301  *
302  * Return 0 if whole of either string is same as another's tail part.
303  */
304 int strtailcmp(const char *s1, const char *s2)
305 {
306 	int i1 = strlen(s1);
307 	int i2 = strlen(s2);
308 	while (--i1 >= 0 && --i2 >= 0) {
309 		if (s1[i1] != s2[i2])
310 			return s1[i1] - s2[i2];
311 	}
312 	return 0;
313 }
314 
315 /**
316  * strxfrchar - Locate and replace character in @s
317  * @s:    The string to be searched/changed.
318  * @from: Source character to be replaced.
319  * @to:   Destination character.
320  *
321  * Return pointer to the changed string.
322  */
323 char *strxfrchar(char *s, char from, char to)
324 {
325 	char *p = s;
326 
327 	while ((p = strchr(p, from)) != NULL)
328 		*p++ = to;
329 
330 	return s;
331 }
332 
333 /**
334  * ltrim - Removes leading whitespace from @s.
335  * @s: The string to be stripped.
336  *
337  * Return pointer to the first non-whitespace character in @s.
338  */
339 char *ltrim(char *s)
340 {
341 	while (isspace(*s))
342 		s++;
343 
344 	return s;
345 }
346 
347 /**
348  * rtrim - Removes trailing whitespace from @s.
349  * @s: The string to be stripped.
350  *
351  * Note that the first trailing whitespace is replaced with a %NUL-terminator
352  * in the given string @s. Returns @s.
353  */
354 char *rtrim(char *s)
355 {
356 	size_t size = strlen(s);
357 	char *end;
358 
359 	if (!size)
360 		return s;
361 
362 	end = s + size - 1;
363 	while (end >= s && isspace(*end))
364 		end--;
365 	*(end + 1) = '\0';
366 
367 	return s;
368 }
369 
370 char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
371 {
372 	/*
373 	 * FIXME: replace this with an expression using log10() when we
374 	 * find a suitable implementation, maybe the one in the dvb drivers...
375 	 *
376 	 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
377 	 */
378 	size_t size = nints * 28 + 1; /* \0 */
379 	size_t i, printed = 0;
380 	char *expr = malloc(size);
381 
382 	if (expr) {
383 		const char *or_and = "||", *eq_neq = "==";
384 		char *e = expr;
385 
386 		if (!in) {
387 			or_and = "&&";
388 			eq_neq = "!=";
389 		}
390 
391 		for (i = 0; i < nints; ++i) {
392 			if (printed == size)
393 				goto out_err_overflow;
394 
395 			if (i > 0)
396 				printed += scnprintf(e + printed, size - printed, " %s ", or_and);
397 			printed += scnprintf(e + printed, size - printed,
398 					     "%s %s %d", var, eq_neq, ints[i]);
399 		}
400 	}
401 
402 	return expr;
403 
404 out_err_overflow:
405 	free(expr);
406 	return NULL;
407 }
408 
409 /* Like strpbrk(), but not break if it is right after a backslash (escaped) */
410 char *strpbrk_esc(char *str, const char *stopset)
411 {
412 	char *ptr;
413 
414 	do {
415 		ptr = strpbrk(str, stopset);
416 		if (ptr == str ||
417 		    (ptr == str + 1 && *(ptr - 1) != '\\'))
418 			break;
419 		str = ptr + 1;
420 	} while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\');
421 
422 	return ptr;
423 }
424 
425 /* Like strdup, but do not copy a single backslash */
426 char *strdup_esc(const char *str)
427 {
428 	char *s, *d, *p, *ret = strdup(str);
429 
430 	if (!ret)
431 		return NULL;
432 
433 	d = strchr(ret, '\\');
434 	if (!d)
435 		return ret;
436 
437 	s = d + 1;
438 	do {
439 		if (*s == '\0') {
440 			*d = '\0';
441 			break;
442 		}
443 		p = strchr(s + 1, '\\');
444 		if (p) {
445 			memmove(d, s, p - s);
446 			d += p - s;
447 			s = p + 1;
448 		} else
449 			memmove(d, s, strlen(s) + 1);
450 	} while (p);
451 
452 	return ret;
453 }
454