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