xref: /linux/tools/perf/util/string.c (revision 1b0975ee3bdd3eb19a47371c26fd7ef8f7f6b599)
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(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 + 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  */
170 bool strglobmatch(const char *str, const char *pat)
171 {
172 	return __match_glob(str, pat, false, false);
173 }
174 
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  */
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  */
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(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) */
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