linux/tools/perf/util/string.c
<<
>>
Prefs
   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
   9const char *graph_dotted_line =
  10        "---------------------------------------------------------------------"
  11        "---------------------------------------------------------------------"
  12        "---------------------------------------------------------------------";
  13const 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 */
  24s64 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
  68out_err:
  69        return -1;
  70}
  71
  72/* Character class matching */
  73static bool __match_charclass(const char *pat, char c, const char **npat)
  74{
  75        bool complement = false, ret = true;
  76
  77        if (*pat == '!') {
  78                complement = true;
  79                pat++;
  80        }
  81        if (*pat++ == c)        /* First character is special */
  82                goto end;
  83
  84        while (*pat && *pat != ']') {   /* Matching */
  85                if (*pat == '-' && *(pat + 1) != ']') { /* Range */
  86                        if (*(pat - 1) <= c && c <= *(pat + 1))
  87                                goto end;
  88                        if (*(pat - 1) > *(pat + 1))
  89                                goto error;
  90                        pat += 2;
  91                } else if (*pat++ == c)
  92                        goto end;
  93        }
  94        if (!*pat)
  95                goto error;
  96        ret = false;
  97
  98end:
  99        while (*pat && *pat != ']')     /* Searching closing */
 100                pat++;
 101        if (!*pat)
 102                goto error;
 103        *npat = pat + 1;
 104        return complement ? !ret : ret;
 105
 106error:
 107        return false;
 108}
 109
 110/* Glob/lazy pattern matching */
 111static bool __match_glob(const char *str, const char *pat, bool ignore_space,
 112                        bool case_ins)
 113{
 114        while (*str && *pat && *pat != '*') {
 115                if (ignore_space) {
 116                        /* Ignore spaces for lazy matching */
 117                        if (isspace(*str)) {
 118                                str++;
 119                                continue;
 120                        }
 121                        if (isspace(*pat)) {
 122                                pat++;
 123                                continue;
 124                        }
 125                }
 126                if (*pat == '?') {      /* Matches any single character */
 127                        str++;
 128                        pat++;
 129                        continue;
 130                } else if (*pat == '[') /* Character classes/Ranges */
 131                        if (__match_charclass(pat + 1, *str, &pat)) {
 132                                str++;
 133                                continue;
 134                        } else
 135                                return false;
 136                else if (*pat == '\\') /* Escaped char match as normal char */
 137                        pat++;
 138                if (case_ins) {
 139                        if (tolower(*str) != tolower(*pat))
 140                                return false;
 141                } else if (*str != *pat)
 142                        return false;
 143                str++;
 144                pat++;
 145        }
 146        /* Check wild card */
 147        if (*pat == '*') {
 148                while (*pat == '*')
 149                        pat++;
 150                if (!*pat)      /* Tail wild card matches all */
 151                        return true;
 152                while (*str)
 153                        if (__match_glob(str++, pat, ignore_space, case_ins))
 154                                return true;
 155        }
 156        return !*str && !*pat;
 157}
 158
 159/**
 160 * strglobmatch - glob expression pattern matching
 161 * @str: the target string to match
 162 * @pat: the pattern string to match
 163 *
 164 * This returns true if the @str matches @pat. @pat can includes wildcards
 165 * ('*','?') and character classes ([CHARS], complementation and ranges are
 166 * also supported). Also, this supports escape character ('\') to use special
 167 * characters as normal character.
 168 *
 169 * Note: if @pat syntax is broken, this always returns false.
 170 */
 171bool strglobmatch(const char *str, const char *pat)
 172{
 173        return __match_glob(str, pat, false, false);
 174}
 175
 176bool strglobmatch_nocase(const char *str, const char *pat)
 177{
 178        return __match_glob(str, pat, false, true);
 179}
 180
 181/**
 182 * strlazymatch - matching pattern strings lazily with glob pattern
 183 * @str: the target string to match
 184 * @pat: the pattern string to match
 185 *
 186 * This is similar to strglobmatch, except this ignores spaces in
 187 * the target string.
 188 */
 189bool strlazymatch(const char *str, const char *pat)
 190{
 191        return __match_glob(str, pat, true, false);
 192}
 193
 194/**
 195 * strtailcmp - Compare the tail of two strings
 196 * @s1: 1st string to be compared
 197 * @s2: 2nd string to be compared
 198 *
 199 * Return 0 if whole of either string is same as another's tail part.
 200 */
 201int strtailcmp(const char *s1, const char *s2)
 202{
 203        int i1 = strlen(s1);
 204        int i2 = strlen(s2);
 205        while (--i1 >= 0 && --i2 >= 0) {
 206                if (s1[i1] != s2[i2])
 207                        return s1[i1] - s2[i2];
 208        }
 209        return 0;
 210}
 211
 212char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
 213{
 214        /*
 215         * FIXME: replace this with an expression using log10() when we
 216         * find a suitable implementation, maybe the one in the dvb drivers...
 217         *
 218         * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
 219         */
 220        size_t size = nints * 28 + 1; /* \0 */
 221        size_t i, printed = 0;
 222        char *expr = malloc(size);
 223
 224        if (expr) {
 225                const char *or_and = "||", *eq_neq = "==";
 226                char *e = expr;
 227
 228                if (!in) {
 229                        or_and = "&&";
 230                        eq_neq = "!=";
 231                }
 232
 233                for (i = 0; i < nints; ++i) {
 234                        if (printed == size)
 235                                goto out_err_overflow;
 236
 237                        if (i > 0)
 238                                printed += scnprintf(e + printed, size - printed, " %s ", or_and);
 239                        printed += scnprintf(e + printed, size - printed,
 240                                             "%s %s %d", var, eq_neq, ints[i]);
 241                }
 242        }
 243
 244        return expr;
 245
 246out_err_overflow:
 247        free(expr);
 248        return NULL;
 249}
 250
 251/* Like strpbrk(), but not break if it is right after a backslash (escaped) */
 252char *strpbrk_esc(char *str, const char *stopset)
 253{
 254        char *ptr;
 255
 256        do {
 257                ptr = strpbrk(str, stopset);
 258                if (ptr == str ||
 259                    (ptr == str + 1 && *(ptr - 1) != '\\'))
 260                        break;
 261                str = ptr + 1;
 262        } while (ptr && *(ptr - 1) == '\\' && *(ptr - 2) != '\\');
 263
 264        return ptr;
 265}
 266
 267/* Like strdup, but do not copy a single backslash */
 268char *strdup_esc(const char *str)
 269{
 270        char *s, *d, *p, *ret = strdup(str);
 271
 272        if (!ret)
 273                return NULL;
 274
 275        d = strchr(ret, '\\');
 276        if (!d)
 277                return ret;
 278
 279        s = d + 1;
 280        do {
 281                if (*s == '\0') {
 282                        *d = '\0';
 283                        break;
 284                }
 285                p = strchr(s + 1, '\\');
 286                if (p) {
 287                        memmove(d, s, p - s);
 288                        d += p - s;
 289                        s = p + 1;
 290                } else
 291                        memmove(d, s, strlen(s) + 1);
 292        } while (p);
 293
 294        return ret;
 295}
 296