162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include "string2.h" 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/string.h> 562306a36Sopenharmony_ci#include <stdlib.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/ctype.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ciconst char *graph_dotted_line = 1062306a36Sopenharmony_ci "---------------------------------------------------------------------" 1162306a36Sopenharmony_ci "---------------------------------------------------------------------" 1262306a36Sopenharmony_ci "---------------------------------------------------------------------"; 1362306a36Sopenharmony_ciconst char *dots = 1462306a36Sopenharmony_ci "....................................................................." 1562306a36Sopenharmony_ci "....................................................................." 1662306a36Sopenharmony_ci "....................................................................."; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * perf_atoll() 2062306a36Sopenharmony_ci * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") 2162306a36Sopenharmony_ci * and return its numeric value 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cis64 perf_atoll(const char *str) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci s64 length; 2662306a36Sopenharmony_ci char *p; 2762306a36Sopenharmony_ci char c; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (!isdigit(str[0])) 3062306a36Sopenharmony_ci goto out_err; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci length = strtoll(str, &p, 10); 3362306a36Sopenharmony_ci switch (c = *p++) { 3462306a36Sopenharmony_ci case 'b': case 'B': 3562306a36Sopenharmony_ci if (*p) 3662306a36Sopenharmony_ci goto out_err; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci fallthrough; 3962306a36Sopenharmony_ci case '\0': 4062306a36Sopenharmony_ci return length; 4162306a36Sopenharmony_ci default: 4262306a36Sopenharmony_ci goto out_err; 4362306a36Sopenharmony_ci /* two-letter suffices */ 4462306a36Sopenharmony_ci case 'k': case 'K': 4562306a36Sopenharmony_ci length <<= 10; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case 'm': case 'M': 4862306a36Sopenharmony_ci length <<= 20; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci case 'g': case 'G': 5162306a36Sopenharmony_ci length <<= 30; 5262306a36Sopenharmony_ci break; 5362306a36Sopenharmony_ci case 't': case 'T': 5462306a36Sopenharmony_ci length <<= 40; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci /* we want the cases to match */ 5862306a36Sopenharmony_ci if (islower(c)) { 5962306a36Sopenharmony_ci if (strcmp(p, "b") != 0) 6062306a36Sopenharmony_ci goto out_err; 6162306a36Sopenharmony_ci } else { 6262306a36Sopenharmony_ci if (strcmp(p, "B") != 0) 6362306a36Sopenharmony_ci goto out_err; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci return length; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ciout_err: 6862306a36Sopenharmony_ci return -1; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Character class matching */ 7262306a36Sopenharmony_cistatic bool __match_charclass(const char *pat, char c, const char **npat) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci bool complement = false, ret = true; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (*pat == '!') { 7762306a36Sopenharmony_ci complement = true; 7862306a36Sopenharmony_ci pat++; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci if (*pat++ == c) /* First character is special */ 8162306a36Sopenharmony_ci goto end; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci while (*pat && *pat != ']') { /* Matching */ 8462306a36Sopenharmony_ci if (*pat == '-' && *(pat + 1) != ']') { /* Range */ 8562306a36Sopenharmony_ci if (*(pat - 1) <= c && c <= *(pat + 1)) 8662306a36Sopenharmony_ci goto end; 8762306a36Sopenharmony_ci if (*(pat - 1) > *(pat + 1)) 8862306a36Sopenharmony_ci goto error; 8962306a36Sopenharmony_ci pat += 2; 9062306a36Sopenharmony_ci } else if (*pat++ == c) 9162306a36Sopenharmony_ci goto end; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (!*pat) 9462306a36Sopenharmony_ci goto error; 9562306a36Sopenharmony_ci ret = false; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciend: 9862306a36Sopenharmony_ci while (*pat && *pat != ']') /* Searching closing */ 9962306a36Sopenharmony_ci pat++; 10062306a36Sopenharmony_ci if (!*pat) 10162306a36Sopenharmony_ci goto error; 10262306a36Sopenharmony_ci *npat = pat + 1; 10362306a36Sopenharmony_ci return complement ? !ret : ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cierror: 10662306a36Sopenharmony_ci return false; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Glob/lazy pattern matching */ 11062306a36Sopenharmony_cistatic bool __match_glob(const char *str, const char *pat, bool ignore_space, 11162306a36Sopenharmony_ci bool case_ins) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci while (*str && *pat && *pat != '*') { 11462306a36Sopenharmony_ci if (ignore_space) { 11562306a36Sopenharmony_ci /* Ignore spaces for lazy matching */ 11662306a36Sopenharmony_ci if (isspace(*str)) { 11762306a36Sopenharmony_ci str++; 11862306a36Sopenharmony_ci continue; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci if (isspace(*pat)) { 12162306a36Sopenharmony_ci pat++; 12262306a36Sopenharmony_ci continue; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci if (*pat == '?') { /* Matches any single character */ 12662306a36Sopenharmony_ci str++; 12762306a36Sopenharmony_ci pat++; 12862306a36Sopenharmony_ci continue; 12962306a36Sopenharmony_ci } else if (*pat == '[') /* Character classes/Ranges */ 13062306a36Sopenharmony_ci if (__match_charclass(pat + 1, *str, &pat)) { 13162306a36Sopenharmony_ci str++; 13262306a36Sopenharmony_ci continue; 13362306a36Sopenharmony_ci } else 13462306a36Sopenharmony_ci return false; 13562306a36Sopenharmony_ci else if (*pat == '\\') /* Escaped char match as normal char */ 13662306a36Sopenharmony_ci pat++; 13762306a36Sopenharmony_ci if (case_ins) { 13862306a36Sopenharmony_ci if (tolower(*str) != tolower(*pat)) 13962306a36Sopenharmony_ci return false; 14062306a36Sopenharmony_ci } else if (*str != *pat) 14162306a36Sopenharmony_ci return false; 14262306a36Sopenharmony_ci str++; 14362306a36Sopenharmony_ci pat++; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci /* Check wild card */ 14662306a36Sopenharmony_ci if (*pat == '*') { 14762306a36Sopenharmony_ci while (*pat == '*') 14862306a36Sopenharmony_ci pat++; 14962306a36Sopenharmony_ci if (!*pat) /* Tail wild card matches all */ 15062306a36Sopenharmony_ci return true; 15162306a36Sopenharmony_ci while (*str) 15262306a36Sopenharmony_ci if (__match_glob(str++, pat, ignore_space, case_ins)) 15362306a36Sopenharmony_ci return true; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci return !*str && !*pat; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * strglobmatch - glob expression pattern matching 16062306a36Sopenharmony_ci * @str: the target string to match 16162306a36Sopenharmony_ci * @pat: the pattern string to match 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * This returns true if the @str matches @pat. @pat can includes wildcards 16462306a36Sopenharmony_ci * ('*','?') and character classes ([CHARS], complementation and ranges are 16562306a36Sopenharmony_ci * also supported). Also, this supports escape character ('\') to use special 16662306a36Sopenharmony_ci * characters as normal character. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Note: if @pat syntax is broken, this always returns false. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cibool strglobmatch(const char *str, const char *pat) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci return __match_glob(str, pat, false, false); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cibool strglobmatch_nocase(const char *str, const char *pat) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return __match_glob(str, pat, false, true); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * strlazymatch - matching pattern strings lazily with glob pattern 18262306a36Sopenharmony_ci * @str: the target string to match 18362306a36Sopenharmony_ci * @pat: the pattern string to match 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * This is similar to strglobmatch, except this ignores spaces in 18662306a36Sopenharmony_ci * the target string. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cibool strlazymatch(const char *str, const char *pat) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return __match_glob(str, pat, true, false); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/** 19462306a36Sopenharmony_ci * strtailcmp - Compare the tail of two strings 19562306a36Sopenharmony_ci * @s1: 1st string to be compared 19662306a36Sopenharmony_ci * @s2: 2nd string to be compared 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Return 0 if whole of either string is same as another's tail part. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ciint strtailcmp(const char *s1, const char *s2) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci int i1 = strlen(s1); 20362306a36Sopenharmony_ci int i2 = strlen(s2); 20462306a36Sopenharmony_ci while (--i1 >= 0 && --i2 >= 0) { 20562306a36Sopenharmony_ci if (s1[i1] != s2[i2]) 20662306a36Sopenharmony_ci return s1[i1] - s2[i2]; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cichar *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * FIXME: replace this with an expression using log10() when we 21562306a36Sopenharmony_ci * find a suitable implementation, maybe the one in the dvb drivers... 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci size_t size = nints * 28 + 1; /* \0 */ 22062306a36Sopenharmony_ci size_t i, printed = 0; 22162306a36Sopenharmony_ci char *expr = malloc(size); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (expr) { 22462306a36Sopenharmony_ci const char *or_and = "||", *eq_neq = "=="; 22562306a36Sopenharmony_ci char *e = expr; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!in) { 22862306a36Sopenharmony_ci or_and = "&&"; 22962306a36Sopenharmony_ci eq_neq = "!="; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for (i = 0; i < nints; ++i) { 23362306a36Sopenharmony_ci if (printed == size) 23462306a36Sopenharmony_ci goto out_err_overflow; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (i > 0) 23762306a36Sopenharmony_ci printed += scnprintf(e + printed, size - printed, " %s ", or_and); 23862306a36Sopenharmony_ci printed += scnprintf(e + printed, size - printed, 23962306a36Sopenharmony_ci "%s %s %d", var, eq_neq, ints[i]); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return expr; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ciout_err_overflow: 24662306a36Sopenharmony_ci free(expr); 24762306a36Sopenharmony_ci return NULL; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* Like strpbrk(), but not break if it is right after a backslash (escaped) */ 25162306a36Sopenharmony_cichar *strpbrk_esc(char *str, const char *stopset) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci char *ptr; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci do { 25662306a36Sopenharmony_ci ptr = strpbrk(str, stopset); 25762306a36Sopenharmony_ci if (ptr == str || 25862306a36Sopenharmony_ci (ptr == str + 1 && *(ptr - 1) != '\\')) 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci str = ptr + 1; 26162306a36Sopenharmony_ci } while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\'); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return ptr; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* Like strdup, but do not copy a single backslash */ 26762306a36Sopenharmony_cichar *strdup_esc(const char *str) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci char *s, *d, *p, *ret = strdup(str); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (!ret) 27262306a36Sopenharmony_ci return NULL; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci d = strchr(ret, '\\'); 27562306a36Sopenharmony_ci if (!d) 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci s = d + 1; 27962306a36Sopenharmony_ci do { 28062306a36Sopenharmony_ci if (*s == '\0') { 28162306a36Sopenharmony_ci *d = '\0'; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci p = strchr(s + 1, '\\'); 28562306a36Sopenharmony_ci if (p) { 28662306a36Sopenharmony_ci memmove(d, s, p - s); 28762306a36Sopenharmony_ci d += p - s; 28862306a36Sopenharmony_ci s = p + 1; 28962306a36Sopenharmony_ci } else 29062306a36Sopenharmony_ci memmove(d, s, strlen(s) + 1); 29162306a36Sopenharmony_ci } while (p); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciunsigned int hex(char c) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci if (c >= '0' && c <= '9') 29962306a36Sopenharmony_ci return c - '0'; 30062306a36Sopenharmony_ci if (c >= 'a' && c <= 'f') 30162306a36Sopenharmony_ci return c - 'a' + 10; 30262306a36Sopenharmony_ci return c - 'A' + 10; 30362306a36Sopenharmony_ci} 304