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 "sane_ctype.h" 8 9 const char *graph_dotted_line = 10 "---------------------------------------------------------------------" 11 "---------------------------------------------------------------------" 12 "---------------------------------------------------------------------"; 13 const char *dots = 14 "....................................................................." 15 "....................................................................." 16 "....................................................................."; 17 18 #define K 1024LL 19 /* 20 * perf_atoll() 21 * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") 22 * and return its numeric value 23 */ 24 s64 perf_atoll(const char *str) 25 { 26 s64 length; 27 char *p; 28 char c; 29 30 if (!isdigit(str[0])) 31 goto out_err; 32 33 length = strtoll(str, &p, 10); 34 switch (c = *p++) { 35 case 'b': case 'B': 36 if (*p) 37 goto out_err; 38 39 __fallthrough; 40 case '\0': 41 return length; 42 default: 43 goto out_err; 44 /* two-letter suffices */ 45 case 'k': case 'K': 46 length <<= 10; 47 break; 48 case 'm': case 'M': 49 length <<= 20; 50 break; 51 case 'g': case 'G': 52 length <<= 30; 53 break; 54 case 't': case 'T': 55 length <<= 40; 56 break; 57 } 58 /* we want the cases to match */ 59 if (islower(c)) { 60 if (strcmp(p, "b") != 0) 61 goto out_err; 62 } else { 63 if (strcmp(p, "B") != 0) 64 goto out_err; 65 } 66 return length; 67 68 out_err: 69 return -1; 70 } 71 72 /* 73 * Helper function for splitting a string into an argv-like array. 74 * originally copied from lib/argv_split.c 75 */ 76 static const char *skip_sep(const char *cp) 77 { 78 while (*cp && isspace(*cp)) 79 cp++; 80 81 return cp; 82 } 83 84 static const char *skip_arg(const char *cp) 85 { 86 while (*cp && !isspace(*cp)) 87 cp++; 88 89 return cp; 90 } 91 92 static int count_argc(const char *str) 93 { 94 int count = 0; 95 96 while (*str) { 97 str = skip_sep(str); 98 if (*str) { 99 count++; 100 str = skip_arg(str); 101 } 102 } 103 104 return count; 105 } 106 107 /** 108 * argv_free - free an argv 109 * @argv - the argument vector to be freed 110 * 111 * Frees an argv and the strings it points to. 112 */ 113 void argv_free(char **argv) 114 { 115 char **p; 116 for (p = argv; *p; p++) { 117 free(*p); 118 *p = NULL; 119 } 120 121 free(argv); 122 } 123 124 /** 125 * argv_split - split a string at whitespace, returning an argv 126 * @str: the string to be split 127 * @argcp: returned argument count 128 * 129 * Returns an array of pointers to strings which are split out from 130 * @str. This is performed by strictly splitting on white-space; no 131 * quote processing is performed. Multiple whitespace characters are 132 * considered to be a single argument separator. The returned array 133 * is always NULL-terminated. Returns NULL on memory allocation 134 * failure. 135 */ 136 char **argv_split(const char *str, int *argcp) 137 { 138 int argc = count_argc(str); 139 char **argv = calloc(argc + 1, sizeof(*argv)); 140 char **argvp; 141 142 if (argv == NULL) 143 goto out; 144 145 if (argcp) 146 *argcp = argc; 147 148 argvp = argv; 149 150 while (*str) { 151 str = skip_sep(str); 152 153 if (*str) { 154 const char *p = str; 155 char *t; 156 157 str = skip_arg(str); 158 159 t = strndup(p, str-p); 160 if (t == NULL) 161 goto fail; 162 *argvp++ = t; 163 } 164 } 165 *argvp = NULL; 166 167 out: 168 return argv; 169 170 fail: 171 argv_free(argv); 172 return NULL; 173 } 174 175 /* Character class matching */ 176 static bool __match_charclass(const char *pat, char c, const char **npat) 177 { 178 bool complement = false, ret = true; 179 180 if (*pat == '!') { 181 complement = true; 182 pat++; 183 } 184 if (*pat++ == c) /* First character is special */ 185 goto end; 186 187 while (*pat && *pat != ']') { /* Matching */ 188 if (*pat == '-' && *(pat + 1) != ']') { /* Range */ 189 if (*(pat - 1) <= c && c <= *(pat + 1)) 190 goto end; 191 if (*(pat - 1) > *(pat + 1)) 192 goto error; 193 pat += 2; 194 } else if (*pat++ == c) 195 goto end; 196 } 197 if (!*pat) 198 goto error; 199 ret = false; 200 201 end: 202 while (*pat && *pat != ']') /* Searching closing */ 203 pat++; 204 if (!*pat) 205 goto error; 206 *npat = pat + 1; 207 return complement ? !ret : ret; 208 209 error: 210 return false; 211 } 212 213 /* Glob/lazy pattern matching */ 214 static bool __match_glob(const char *str, const char *pat, bool ignore_space, 215 bool case_ins) 216 { 217 while (*str && *pat && *pat != '*') { 218 if (ignore_space) { 219 /* Ignore spaces for lazy matching */ 220 if (isspace(*str)) { 221 str++; 222 continue; 223 } 224 if (isspace(*pat)) { 225 pat++; 226 continue; 227 } 228 } 229 if (*pat == '?') { /* Matches any single character */ 230 str++; 231 pat++; 232 continue; 233 } else if (*pat == '[') /* Character classes/Ranges */ 234 if (__match_charclass(pat + 1, *str, &pat)) { 235 str++; 236 continue; 237 } else 238 return false; 239 else if (*pat == '\\') /* Escaped char match as normal char */ 240 pat++; 241 if (case_ins) { 242 if (tolower(*str) != tolower(*pat)) 243 return false; 244 } else if (*str != *pat) 245 return false; 246 str++; 247 pat++; 248 } 249 /* Check wild card */ 250 if (*pat == '*') { 251 while (*pat == '*') 252 pat++; 253 if (!*pat) /* Tail wild card matches all */ 254 return true; 255 while (*str) 256 if (__match_glob(str++, pat, ignore_space, case_ins)) 257 return true; 258 } 259 return !*str && !*pat; 260 } 261 262 /** 263 * strglobmatch - glob expression pattern matching 264 * @str: the target string to match 265 * @pat: the pattern string to match 266 * 267 * This returns true if the @str matches @pat. @pat can includes wildcards 268 * ('*','?') and character classes ([CHARS], complementation and ranges are 269 * also supported). Also, this supports escape character ('\') to use special 270 * characters as normal character. 271 * 272 * Note: if @pat syntax is broken, this always returns false. 273 */ 274 bool strglobmatch(const char *str, const char *pat) 275 { 276 return __match_glob(str, pat, false, false); 277 } 278 279 bool strglobmatch_nocase(const char *str, const char *pat) 280 { 281 return __match_glob(str, pat, false, true); 282 } 283 284 /** 285 * strlazymatch - matching pattern strings lazily with glob pattern 286 * @str: the target string to match 287 * @pat: the pattern string to match 288 * 289 * This is similar to strglobmatch, except this ignores spaces in 290 * the target string. 291 */ 292 bool strlazymatch(const char *str, const char *pat) 293 { 294 return __match_glob(str, pat, true, false); 295 } 296 297 /** 298 * strtailcmp - Compare the tail of two strings 299 * @s1: 1st string to be compared 300 * @s2: 2nd string to be compared 301 * 302 * Return 0 if whole of either string is same as another's tail part. 303 */ 304 int strtailcmp(const char *s1, const char *s2) 305 { 306 int i1 = strlen(s1); 307 int i2 = strlen(s2); 308 while (--i1 >= 0 && --i2 >= 0) { 309 if (s1[i1] != s2[i2]) 310 return s1[i1] - s2[i2]; 311 } 312 return 0; 313 } 314 315 /** 316 * strxfrchar - Locate and replace character in @s 317 * @s: The string to be searched/changed. 318 * @from: Source character to be replaced. 319 * @to: Destination character. 320 * 321 * Return pointer to the changed string. 322 */ 323 char *strxfrchar(char *s, char from, char to) 324 { 325 char *p = s; 326 327 while ((p = strchr(p, from)) != NULL) 328 *p++ = to; 329 330 return s; 331 } 332 333 /** 334 * ltrim - Removes leading whitespace from @s. 335 * @s: The string to be stripped. 336 * 337 * Return pointer to the first non-whitespace character in @s. 338 */ 339 char *ltrim(char *s) 340 { 341 while (isspace(*s)) 342 s++; 343 344 return s; 345 } 346 347 /** 348 * rtrim - Removes trailing whitespace from @s. 349 * @s: The string to be stripped. 350 * 351 * Note that the first trailing whitespace is replaced with a %NUL-terminator 352 * in the given string @s. Returns @s. 353 */ 354 char *rtrim(char *s) 355 { 356 size_t size = strlen(s); 357 char *end; 358 359 if (!size) 360 return s; 361 362 end = s + size - 1; 363 while (end >= s && isspace(*end)) 364 end--; 365 *(end + 1) = '\0'; 366 367 return s; 368 } 369 370 char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints) 371 { 372 /* 373 * FIXME: replace this with an expression using log10() when we 374 * find a suitable implementation, maybe the one in the dvb drivers... 375 * 376 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators 377 */ 378 size_t size = nints * 28 + 1; /* \0 */ 379 size_t i, printed = 0; 380 char *expr = malloc(size); 381 382 if (expr) { 383 const char *or_and = "||", *eq_neq = "=="; 384 char *e = expr; 385 386 if (!in) { 387 or_and = "&&"; 388 eq_neq = "!="; 389 } 390 391 for (i = 0; i < nints; ++i) { 392 if (printed == size) 393 goto out_err_overflow; 394 395 if (i > 0) 396 printed += scnprintf(e + printed, size - printed, " %s ", or_and); 397 printed += scnprintf(e + printed, size - printed, 398 "%s %s %d", var, eq_neq, ints[i]); 399 } 400 } 401 402 return expr; 403 404 out_err_overflow: 405 free(expr); 406 return NULL; 407 } 408 409 /* Like strpbrk(), but not break if it is right after a backslash (escaped) */ 410 char *strpbrk_esc(char *str, const char *stopset) 411 { 412 char *ptr; 413 414 do { 415 ptr = strpbrk(str, stopset); 416 if (ptr == str || 417 (ptr == str + 1 && *(ptr - 1) != '\\')) 418 break; 419 str = ptr + 1; 420 } while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\'); 421 422 return ptr; 423 } 424 425 /* Like strdup, but do not copy a single backslash */ 426 char *strdup_esc(const char *str) 427 { 428 char *s, *d, *p, *ret = strdup(str); 429 430 if (!ret) 431 return NULL; 432 433 d = strchr(ret, '\\'); 434 if (!d) 435 return ret; 436 437 s = d + 1; 438 do { 439 if (*s == '\0') { 440 *d = '\0'; 441 break; 442 } 443 p = strchr(s + 1, '\\'); 444 if (p) { 445 memmove(d, s, p - s); 446 d += p - s; 447 s = p + 1; 448 } else 449 memmove(d, s, strlen(s) + 1); 450 } while (p); 451 452 return ret; 453 } 454