xref: /kernel/linux/linux-5.10/tools/perf/util/string.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include "string2.h"
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <linux/string.h>
58c2ecf20Sopenharmony_ci#include <stdlib.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/ctype.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ciconst char *graph_dotted_line =
108c2ecf20Sopenharmony_ci	"---------------------------------------------------------------------"
118c2ecf20Sopenharmony_ci	"---------------------------------------------------------------------"
128c2ecf20Sopenharmony_ci	"---------------------------------------------------------------------";
138c2ecf20Sopenharmony_ciconst char *dots =
148c2ecf20Sopenharmony_ci	"....................................................................."
158c2ecf20Sopenharmony_ci	"....................................................................."
168c2ecf20Sopenharmony_ci	".....................................................................";
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define K 1024LL
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * perf_atoll()
218c2ecf20Sopenharmony_ci * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
228c2ecf20Sopenharmony_ci * and return its numeric value
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cis64 perf_atoll(const char *str)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	s64 length;
278c2ecf20Sopenharmony_ci	char *p;
288c2ecf20Sopenharmony_ci	char c;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (!isdigit(str[0]))
318c2ecf20Sopenharmony_ci		goto out_err;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	length = strtoll(str, &p, 10);
348c2ecf20Sopenharmony_ci	switch (c = *p++) {
358c2ecf20Sopenharmony_ci		case 'b': case 'B':
368c2ecf20Sopenharmony_ci			if (*p)
378c2ecf20Sopenharmony_ci				goto out_err;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci			__fallthrough;
408c2ecf20Sopenharmony_ci		case '\0':
418c2ecf20Sopenharmony_ci			return length;
428c2ecf20Sopenharmony_ci		default:
438c2ecf20Sopenharmony_ci			goto out_err;
448c2ecf20Sopenharmony_ci		/* two-letter suffices */
458c2ecf20Sopenharmony_ci		case 'k': case 'K':
468c2ecf20Sopenharmony_ci			length <<= 10;
478c2ecf20Sopenharmony_ci			break;
488c2ecf20Sopenharmony_ci		case 'm': case 'M':
498c2ecf20Sopenharmony_ci			length <<= 20;
508c2ecf20Sopenharmony_ci			break;
518c2ecf20Sopenharmony_ci		case 'g': case 'G':
528c2ecf20Sopenharmony_ci			length <<= 30;
538c2ecf20Sopenharmony_ci			break;
548c2ecf20Sopenharmony_ci		case 't': case 'T':
558c2ecf20Sopenharmony_ci			length <<= 40;
568c2ecf20Sopenharmony_ci			break;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	/* we want the cases to match */
598c2ecf20Sopenharmony_ci	if (islower(c)) {
608c2ecf20Sopenharmony_ci		if (strcmp(p, "b") != 0)
618c2ecf20Sopenharmony_ci			goto out_err;
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		if (strcmp(p, "B") != 0)
648c2ecf20Sopenharmony_ci			goto out_err;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	return length;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ciout_err:
698c2ecf20Sopenharmony_ci	return -1;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Character class matching */
738c2ecf20Sopenharmony_cistatic bool __match_charclass(const char *pat, char c, const char **npat)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	bool complement = false, ret = true;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (*pat == '!') {
788c2ecf20Sopenharmony_ci		complement = true;
798c2ecf20Sopenharmony_ci		pat++;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	if (*pat++ == c)	/* First character is special */
828c2ecf20Sopenharmony_ci		goto end;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	while (*pat && *pat != ']') {	/* Matching */
858c2ecf20Sopenharmony_ci		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
868c2ecf20Sopenharmony_ci			if (*(pat - 1) <= c && c <= *(pat + 1))
878c2ecf20Sopenharmony_ci				goto end;
888c2ecf20Sopenharmony_ci			if (*(pat - 1) > *(pat + 1))
898c2ecf20Sopenharmony_ci				goto error;
908c2ecf20Sopenharmony_ci			pat += 2;
918c2ecf20Sopenharmony_ci		} else if (*pat++ == c)
928c2ecf20Sopenharmony_ci			goto end;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci	if (!*pat)
958c2ecf20Sopenharmony_ci		goto error;
968c2ecf20Sopenharmony_ci	ret = false;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciend:
998c2ecf20Sopenharmony_ci	while (*pat && *pat != ']')	/* Searching closing */
1008c2ecf20Sopenharmony_ci		pat++;
1018c2ecf20Sopenharmony_ci	if (!*pat)
1028c2ecf20Sopenharmony_ci		goto error;
1038c2ecf20Sopenharmony_ci	*npat = pat + 1;
1048c2ecf20Sopenharmony_ci	return complement ? !ret : ret;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cierror:
1078c2ecf20Sopenharmony_ci	return false;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* Glob/lazy pattern matching */
1118c2ecf20Sopenharmony_cistatic bool __match_glob(const char *str, const char *pat, bool ignore_space,
1128c2ecf20Sopenharmony_ci			bool case_ins)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	while (*str && *pat && *pat != '*') {
1158c2ecf20Sopenharmony_ci		if (ignore_space) {
1168c2ecf20Sopenharmony_ci			/* Ignore spaces for lazy matching */
1178c2ecf20Sopenharmony_ci			if (isspace(*str)) {
1188c2ecf20Sopenharmony_ci				str++;
1198c2ecf20Sopenharmony_ci				continue;
1208c2ecf20Sopenharmony_ci			}
1218c2ecf20Sopenharmony_ci			if (isspace(*pat)) {
1228c2ecf20Sopenharmony_ci				pat++;
1238c2ecf20Sopenharmony_ci				continue;
1248c2ecf20Sopenharmony_ci			}
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		if (*pat == '?') {	/* Matches any single character */
1278c2ecf20Sopenharmony_ci			str++;
1288c2ecf20Sopenharmony_ci			pat++;
1298c2ecf20Sopenharmony_ci			continue;
1308c2ecf20Sopenharmony_ci		} else if (*pat == '[')	/* Character classes/Ranges */
1318c2ecf20Sopenharmony_ci			if (__match_charclass(pat + 1, *str, &pat)) {
1328c2ecf20Sopenharmony_ci				str++;
1338c2ecf20Sopenharmony_ci				continue;
1348c2ecf20Sopenharmony_ci			} else
1358c2ecf20Sopenharmony_ci				return false;
1368c2ecf20Sopenharmony_ci		else if (*pat == '\\') /* Escaped char match as normal char */
1378c2ecf20Sopenharmony_ci			pat++;
1388c2ecf20Sopenharmony_ci		if (case_ins) {
1398c2ecf20Sopenharmony_ci			if (tolower(*str) != tolower(*pat))
1408c2ecf20Sopenharmony_ci				return false;
1418c2ecf20Sopenharmony_ci		} else if (*str != *pat)
1428c2ecf20Sopenharmony_ci			return false;
1438c2ecf20Sopenharmony_ci		str++;
1448c2ecf20Sopenharmony_ci		pat++;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	/* Check wild card */
1478c2ecf20Sopenharmony_ci	if (*pat == '*') {
1488c2ecf20Sopenharmony_ci		while (*pat == '*')
1498c2ecf20Sopenharmony_ci			pat++;
1508c2ecf20Sopenharmony_ci		if (!*pat)	/* Tail wild card matches all */
1518c2ecf20Sopenharmony_ci			return true;
1528c2ecf20Sopenharmony_ci		while (*str)
1538c2ecf20Sopenharmony_ci			if (__match_glob(str++, pat, ignore_space, case_ins))
1548c2ecf20Sopenharmony_ci				return true;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	return !*str && !*pat;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/**
1608c2ecf20Sopenharmony_ci * strglobmatch - glob expression pattern matching
1618c2ecf20Sopenharmony_ci * @str: the target string to match
1628c2ecf20Sopenharmony_ci * @pat: the pattern string to match
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * This returns true if the @str matches @pat. @pat can includes wildcards
1658c2ecf20Sopenharmony_ci * ('*','?') and character classes ([CHARS], complementation and ranges are
1668c2ecf20Sopenharmony_ci * also supported). Also, this supports escape character ('\') to use special
1678c2ecf20Sopenharmony_ci * characters as normal character.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * Note: if @pat syntax is broken, this always returns false.
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cibool strglobmatch(const char *str, const char *pat)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return __match_glob(str, pat, false, false);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cibool strglobmatch_nocase(const char *str, const char *pat)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	return __match_glob(str, pat, false, true);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/**
1828c2ecf20Sopenharmony_ci * strlazymatch - matching pattern strings lazily with glob pattern
1838c2ecf20Sopenharmony_ci * @str: the target string to match
1848c2ecf20Sopenharmony_ci * @pat: the pattern string to match
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * This is similar to strglobmatch, except this ignores spaces in
1878c2ecf20Sopenharmony_ci * the target string.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cibool strlazymatch(const char *str, const char *pat)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	return __match_glob(str, pat, true, false);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/**
1958c2ecf20Sopenharmony_ci * strtailcmp - Compare the tail of two strings
1968c2ecf20Sopenharmony_ci * @s1: 1st string to be compared
1978c2ecf20Sopenharmony_ci * @s2: 2nd string to be compared
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * Return 0 if whole of either string is same as another's tail part.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_ciint strtailcmp(const char *s1, const char *s2)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	int i1 = strlen(s1);
2048c2ecf20Sopenharmony_ci	int i2 = strlen(s2);
2058c2ecf20Sopenharmony_ci	while (--i1 >= 0 && --i2 >= 0) {
2068c2ecf20Sopenharmony_ci		if (s1[i1] != s2[i2])
2078c2ecf20Sopenharmony_ci			return s1[i1] - s2[i2];
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cichar *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	/*
2158c2ecf20Sopenharmony_ci	 * FIXME: replace this with an expression using log10() when we
2168c2ecf20Sopenharmony_ci	 * find a suitable implementation, maybe the one in the dvb drivers...
2178c2ecf20Sopenharmony_ci	 *
2188c2ecf20Sopenharmony_ci	 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
2198c2ecf20Sopenharmony_ci	 */
2208c2ecf20Sopenharmony_ci	size_t size = nints * 28 + 1; /* \0 */
2218c2ecf20Sopenharmony_ci	size_t i, printed = 0;
2228c2ecf20Sopenharmony_ci	char *expr = malloc(size);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (expr) {
2258c2ecf20Sopenharmony_ci		const char *or_and = "||", *eq_neq = "==";
2268c2ecf20Sopenharmony_ci		char *e = expr;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		if (!in) {
2298c2ecf20Sopenharmony_ci			or_and = "&&";
2308c2ecf20Sopenharmony_ci			eq_neq = "!=";
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		for (i = 0; i < nints; ++i) {
2348c2ecf20Sopenharmony_ci			if (printed == size)
2358c2ecf20Sopenharmony_ci				goto out_err_overflow;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci			if (i > 0)
2388c2ecf20Sopenharmony_ci				printed += scnprintf(e + printed, size - printed, " %s ", or_and);
2398c2ecf20Sopenharmony_ci			printed += scnprintf(e + printed, size - printed,
2408c2ecf20Sopenharmony_ci					     "%s %s %d", var, eq_neq, ints[i]);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return expr;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciout_err_overflow:
2478c2ecf20Sopenharmony_ci	free(expr);
2488c2ecf20Sopenharmony_ci	return NULL;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* Like strpbrk(), but not break if it is right after a backslash (escaped) */
2528c2ecf20Sopenharmony_cichar *strpbrk_esc(char *str, const char *stopset)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	char *ptr;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	do {
2578c2ecf20Sopenharmony_ci		ptr = strpbrk(str, stopset);
2588c2ecf20Sopenharmony_ci		if (ptr == str ||
2598c2ecf20Sopenharmony_ci		    (ptr == str + 1 && *(ptr - 1) != '\\'))
2608c2ecf20Sopenharmony_ci			break;
2618c2ecf20Sopenharmony_ci		str = ptr + 1;
2628c2ecf20Sopenharmony_ci	} while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\');
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return ptr;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/* Like strdup, but do not copy a single backslash */
2688c2ecf20Sopenharmony_cichar *strdup_esc(const char *str)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	char *s, *d, *p, *ret = strdup(str);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (!ret)
2738c2ecf20Sopenharmony_ci		return NULL;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	d = strchr(ret, '\\');
2768c2ecf20Sopenharmony_ci	if (!d)
2778c2ecf20Sopenharmony_ci		return ret;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	s = d + 1;
2808c2ecf20Sopenharmony_ci	do {
2818c2ecf20Sopenharmony_ci		if (*s == '\0') {
2828c2ecf20Sopenharmony_ci			*d = '\0';
2838c2ecf20Sopenharmony_ci			break;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci		p = strchr(s + 1, '\\');
2868c2ecf20Sopenharmony_ci		if (p) {
2878c2ecf20Sopenharmony_ci			memmove(d, s, p - s);
2888c2ecf20Sopenharmony_ci			d += p - s;
2898c2ecf20Sopenharmony_ci			s = p + 1;
2908c2ecf20Sopenharmony_ci		} else
2918c2ecf20Sopenharmony_ci			memmove(d, s, strlen(s) + 1);
2928c2ecf20Sopenharmony_ci	} while (p);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return ret;
2958c2ecf20Sopenharmony_ci}
296