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) { 258 /* stopset not in str. */ 259 break; 260 } 261 if (ptr == str) { 262 /* stopset character is first in str. */ 263 break; 264 } 265 if (ptr == str + 1 && str[0] != '\\') { 266 /* stopset chacter is second and wasn't preceded by a '\'. */ 267 break; 268 } 269 str = ptr + 1; 270 } while (ptr[-1] == '\\' && ptr[-2] != '\\'); 271 272 return ptr; 273 } 274 275 /* Like strpbrk_esc(), but not break if it is quoted with single/double quotes */ 276 char *strpbrk_esq(char *str, const char *stopset) 277 { 278 char *_stopset = NULL; 279 char *ptr; 280 const char *squote = "'"; 281 const char *dquote = "\""; 282 283 if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0) 284 return NULL; 285 286 do { 287 ptr = strpbrk_esc(str, _stopset); 288 if (!ptr) 289 break; 290 if (*ptr == *squote) 291 ptr = strpbrk_esc(ptr + 1, squote); 292 else if (*ptr == *dquote) 293 ptr = strpbrk_esc(ptr + 1, dquote); 294 else 295 break; 296 str = ptr + 1; 297 } while (ptr); 298 299 free(_stopset); 300 return ptr; 301 } 302 303 /* Like strdup, but do not copy a single backslash */ 304 char *strdup_esc(const char *str) 305 { 306 char *s, *d, *p, *ret = strdup(str); 307 308 if (!ret) 309 return NULL; 310 311 d = strchr(ret, '\\'); 312 if (!d) 313 return ret; 314 315 s = d + 1; 316 do { 317 if (*s == '\0') { 318 *d = '\0'; 319 break; 320 } 321 p = strchr(s + 1, '\\'); 322 if (p) { 323 memmove(d, s, p - s); 324 d += p - s; 325 s = p + 1; 326 } else 327 memmove(d, s, strlen(s) + 1); 328 } while (p); 329 330 return ret; 331 } 332 333 /* Remove backslash right before quote and return next quote address. */ 334 static char *remove_consumed_esc(char *str, int len, int quote) 335 { 336 char *ptr = str, *end = str + len; 337 338 while (*ptr != quote && ptr < end) { 339 if (*ptr == '\\' && *(ptr + 1) == quote) { 340 memmove(ptr, ptr + 1, end - (ptr + 1)); 341 /* now *ptr is `quote`. */ 342 end--; 343 } 344 ptr++; 345 } 346 347 return *ptr == quote ? ptr : NULL; 348 } 349 350 /* 351 * Like strdup_esc, but keep quoted string as it is (and single backslash 352 * before quote is removed). If there is no closed quote, return NULL. 353 */ 354 char *strdup_esq(const char *str) 355 { 356 char *d, *ret; 357 358 /* If there is no quote, return normal strdup_esc() */ 359 d = strpbrk_esc((char *)str, "\"'"); 360 if (!d) 361 return strdup_esc(str); 362 363 ret = strdup(str); 364 if (!ret) 365 return NULL; 366 367 d = ret; 368 do { 369 d = strpbrk(d, "\\\"\'"); 370 if (!d) 371 break; 372 373 if (*d == '"' || *d == '\'') { 374 /* This is non-escaped quote */ 375 int quote = *d; 376 int len = strlen(d + 1) + 1; 377 378 /* 379 * Remove the start quote and remove consumed escape (backslash 380 * before quote) and remove the end quote. If there is no end 381 * quote, it is the input error. 382 */ 383 memmove(d, d + 1, len); 384 d = remove_consumed_esc(d, len, quote); 385 if (!d) 386 goto error; 387 memmove(d, d + 1, strlen(d + 1) + 1); 388 } 389 if (*d == '\\') { 390 memmove(d, d + 1, strlen(d + 1) + 1); 391 if (*d == '\\') { 392 /* double backslash -- keep the second one. */ 393 d++; 394 } 395 } 396 } while (*d != '\0'); 397 398 return ret; 399 400 error: 401 free(ret); 402 return NULL; 403 } 404 405 unsigned int hex(char c) 406 { 407 if (c >= '0' && c <= '9') 408 return c - '0'; 409 if (c >= 'a' && c <= 'f') 410 return c - 'a' + 10; 411 return c - 'A' + 10; 412 } 413 414 /* 415 * Replace all occurrences of character 'needle' in string 'haystack' with 416 * string 'replace' 417 * 418 * The new string could be longer so a new string is returned which must be 419 * freed. 420 */ 421 char *strreplace_chars(char needle, const char *haystack, const char *replace) 422 { 423 int replace_len = strlen(replace); 424 char *new_s, *to; 425 const char *loc = strchr(haystack, needle); 426 const char *from = haystack; 427 int num = 0; 428 429 /* Count occurrences */ 430 while (loc) { 431 loc = strchr(loc + 1, needle); 432 num++; 433 } 434 435 /* Allocate enough space for replacements and reset first location */ 436 new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1)); 437 if (!new_s) 438 return NULL; 439 loc = strchr(haystack, needle); 440 to = new_s; 441 442 while (loc) { 443 /* Copy original string up to found char and update positions */ 444 memcpy(to, from, 1 + loc - from); 445 to += loc - from; 446 from = loc + 1; 447 448 /* Copy replacement string and update positions */ 449 memcpy(to, replace, replace_len); 450 to += replace_len; 451 452 /* needle next occurrence or end of string */ 453 loc = strchr(from, needle); 454 } 455 456 /* Copy any remaining chars + null */ 457 strcpy(to, from); 458 459 return new_s; 460 } 461