352 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "string2.h"3#include <linux/kernel.h>4#include <linux/string.h>5#include <stdlib.h>6 7#include <linux/ctype.h>8 9const char *graph_dotted_line =10 "---------------------------------------------------------------------"11 "---------------------------------------------------------------------"12 "---------------------------------------------------------------------";13const 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 value22 */23s64 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 67out_err:68 return -1;69}70 71/* Character class matching */72static 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 97end:98 while (*pat && *pat != ']') /* Searching closing */99 pat++;100 if (!*pat)101 goto error;102 *npat = pat + 1;103 return complement ? !ret : ret;104 105error:106 return false;107}108 109/* Glob/lazy pattern matching */110static 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 } else134 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 matching160 * @str: the target string to match161 * @pat: the pattern string to match162 *163 * This returns true if the @str matches @pat. @pat can includes wildcards164 * ('*','?') and character classes ([CHARS], complementation and ranges are165 * also supported). Also, this supports escape character ('\') to use special166 * characters as normal character.167 *168 * Note: if @pat syntax is broken, this always returns false.169 */170bool strglobmatch(const char *str, const char *pat)171{172 return __match_glob(str, pat, false, false);173}174 175bool 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 pattern182 * @str: the target string to match183 * @pat: the pattern string to match184 *185 * This is similar to strglobmatch, except this ignores spaces in186 * the target string.187 */188bool 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 strings195 * @s1: 1st string to be compared196 * @s2: 2nd string to be compared197 *198 * Return 0 if whole of either string is same as another's tail part.199 */200int 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 211char *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 we215 * find a suitable implementation, maybe the one in the dvb drivers...216 *217 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators218 */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 245out_err_overflow:246 free(expr);247 return NULL;248}249 250/* Like strpbrk(), but not break if it is right after a backslash (escaped) */251char *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 */267char *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 } else290 memmove(d, s, strlen(s) + 1);291 } while (p);292 293 return ret;294}295 296unsigned 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 305/*306 * Replace all occurrences of character 'needle' in string 'haystack' with307 * string 'replace'308 *309 * The new string could be longer so a new string is returned which must be310 * freed.311 */312char *strreplace_chars(char needle, const char *haystack, const char *replace)313{314 int replace_len = strlen(replace);315 char *new_s, *to;316 const char *loc = strchr(haystack, needle);317 const char *from = haystack;318 int num = 0;319 320 /* Count occurrences */321 while (loc) {322 loc = strchr(loc + 1, needle);323 num++;324 }325 326 /* Allocate enough space for replacements and reset first location */327 new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1));328 if (!new_s)329 return NULL;330 loc = strchr(haystack, needle);331 to = new_s;332 333 while (loc) {334 /* Copy original string up to found char and update positions */335 memcpy(to, from, 1 + loc - from);336 to += loc - from;337 from = loc + 1;338 339 /* Copy replacement string and update positions */340 memcpy(to, replace, replace_len);341 to += replace_len;342 343 /* needle next occurrence or end of string */344 loc = strchr(from, needle);345 }346 347 /* Copy any remaining chars + null */348 strcpy(to, from);349 350 return new_s;351}352