text: // SPDX-License-Identifier: GPL-2.0
  #include "string2.h"
  #include <linux/kernel.h>
  #include <linux/string.h>
  #include <stdlib.h>
  
  #include "sane_ctype.h"
  
  #define K 1024LL
  /*
   * perf_atoll()
   * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
   * and return its numeric value
   */
  s64 perf_atoll(const char *str)
  {
  	s64 length;
  	char *p;
  	char c;
  
  	if (!isdigit(str[0]))
  		goto out_err;
  
  	length = strtoll(str, &p, 10);
  	switch (c = *p++) {
  		case 'b': case 'B':
  			if (*p)
  				goto out_err;
  
  			__fallthrough;
  		case '\0':
  			return length;
  		default:
  			goto out_err;
  		/* two-letter suffices */
  		case 'k': case 'K':
  			length <<= 10;
  			break;
  		case 'm': case 'M':
  			length <<= 20;
  			break;
  		case 'g': case 'G':
  			length <<= 30;
  			break;
  		case 't': case 'T':
  			length <<= 40;
  			break;
  	}
  	/* we want the cases to match */
  	if (islower(c)) {
  		if (strcmp(p, "b") != 0)
  			goto out_err;
  	} else {
  		if (strcmp(p, "B") != 0)
  			goto out_err;
  	}
  	return length;
  
  out_err:
  	return -1;
  }
  
  /*
   * Helper function for splitting a string into an argv-like array.
   * originally copied from lib/argv_split.c
   */
  static const char *skip_sep(const char *cp)
  {
  	while (*cp && isspace(*cp))
  		cp++;
  
  	return cp;
  }
  
  static const char *skip_arg(const char *cp)
  {
  	while (*cp && !isspace(*cp))
  		cp++;
  
  	return cp;
  }
  
  static int count_argc(const char *str)
  {
  	int count = 0;
  
  	while (*str) {
  		str = skip_sep(str);
  		if (*str) {
  			count++;
  			str = skip_arg(str);
  		}
  	}
  
  	return count;
  }
  
  /**
   * argv_free - free an argv
   * @argv - the argument vector to be freed
   *
   * Frees an argv and the strings it points to.
   */
  void argv_free(char **argv)
  {
  	char **p;
  	for (p = argv; *p; p++) {
  		free(*p);
  		*p = NULL;
  	}
  
  	free(argv);
  }
  
  /**
   * argv_split - split a string at whitespace, returning an argv
   * @str: the string to be split
   * @argcp: returned argument count
   *
   * Returns an array of pointers to strings which are split out from
   * @str.  This is performed by strictly splitting on white-space; no
   * quote processing is performed.  Multiple whitespace characters are
   * considered to be a single argument separator.  The returned array
   * is always NULL-terminated.  Returns NULL on memory allocation
   * failure.
   */
  char **argv_split(const char *str, int *argcp)
  {
  	int argc = count_argc(str);
  	char **argv = calloc(argc + 1, sizeof(*argv));
  	char **argvp;
  
  	if (argv == NULL)
  		goto out;
  
  	if (argcp)
  		*argcp = argc;
  
  	argvp = argv;
  
  	while (*str) {
  		str = skip_sep(str);
  
  		if (*str) {
  			const char *p = str;
  			char *t;
  
  			str = skip_arg(str);
  
  			t = strndup(p, str-p);
  			if (t == NULL)
  				goto fail;
  			*argvp++ = t;
  		}
  	}
  	*argvp = NULL;
  
  out:
  	return argv;
  
  fail:
  	argv_free(argv);
  	return NULL;
  }
  
  /* Character class matching */
  static bool __match_charclass(const char *pat, char c, const char **npat)
  {
  	bool complement = false, ret = true;
  
  	if (*pat == '!') {
  		complement = true;
  		pat++;
  	}
  	if (*pat++ == c)	/* First character is special */
  		goto end;
  
  	while (*pat && *pat != ']') {	/* Matching */
  		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
  			if (*(pat - 1) <= c && c <= *(pat + 1))
  				goto end;
  			if (*(pat - 1) > *(pat + 1))
  				goto error;
  			pat += 2;
  		} else if (*pat++ == c)
  			goto end;
  	}
  	if (!*pat)
  		goto error;
  	ret = false;
  
  end:
  	while (*pat && *pat != ']')	/* Searching closing */
  		pat++;
  	if (!*pat)
  		goto error;
  	*npat = pat + 1;
  	return complement ? !ret : ret;
  
  error:
  	return false;
  }
  
  /* Glob/lazy pattern matching */
  static bool __match_glob(const char *str, const char *pat, bool ignore_space,
  			bool case_ins)
  {
  	while (*str && *pat && *pat != '*') {
  		if (ignore_space) {
  			/* Ignore spaces for lazy matching */
  			if (isspace(*str)) {
  				str++;
  				continue;
  			}
  			if (isspace(*pat)) {
  				pat++;
  				continue;
  			}
  		}
  		if (*pat == '?') {	/* Matches any single character */
  			str++;
  			pat++;
  			continue;
  		} else if (*pat == '[')	/* Character classes/Ranges */
  			if (__match_charclass(pat + 1, *str, &pat)) {
  				str++;
  				continue;
  			} else
  				return false;
  		else if (*pat == '\\') /* Escaped char match as normal char */
  			pat++;
  		if (case_ins) {
  			if (tolower(*str) != tolower(*pat))
  				return false;
  		} else if (*str != *pat)
  			return false;
  		str++;
  		pat++;
  	}
  	/* Check wild card */
  	if (*pat == '*') {
  		while (*pat == '*')
  			pat++;
  		if (!*pat)	/* Tail wild card matches all */
  			return true;
  		while (*str)
  			if (__match_glob(str++, pat, ignore_space, case_ins))
  				return true;
  	}
  	return !*str && !*pat;
  }
  
  /**
   * strglobmatch - glob expression pattern matching
   * @str: the target string to match
   * @pat: the pattern string to match
   *
   * This returns true if the @str matches @pat. @pat can includes wildcards
   * ('*','?') and character classes ([CHARS], complementation and ranges are
   * also supported). Also, this supports escape character ('\') to use special
   * characters as normal character.
   *
   * Note: if @pat syntax is broken, this always returns false.
   */
  bool strglobmatch(const char *str, const char *pat)
  {
  	return __match_glob(str, pat, false, false);
  }
  
  bool strglobmatch_nocase(const char *str, const char *pat)
  {
  	return __match_glob(str, pat, false, true);
  }
  
  /**
   * strlazymatch - matching pattern strings lazily with glob pattern
   * @str: the target string to match
   * @pat: the pattern string to match
   *
   * This is similar to strglobmatch, except this ignores spaces in
   * the target string.
   */
  bool strlazymatch(const char *str, const char *pat)
  {
  	return __match_glob(str, pat, true, false);
  }
  
  /**
   * strtailcmp - Compare the tail of two strings
   * @s1: 1st string to be compared
   * @s2: 2nd string to be compared
   *
   * Return 0 if whole of either string is same as another's tail part.
   */
  int strtailcmp(const char *s1, const char *s2)
  {
  	int i1 = strlen(s1);
  	int i2 = strlen(s2);
  	while (--i1 >= 0 && --i2 >= 0) {
  		if (s1[i1] != s2[i2])
  			return s1[i1] - s2[i2];
  	}
  	return 0;
  }
  
  /**
   * strxfrchar - Locate and replace character in @s
   * @s:    The string to be searched/changed.
   * @from: Source character to be replaced.
   * @to:   Destination character.
   *
   * Return pointer to the changed string.
   */
  char *strxfrchar(char *s, char from, char to)
  {
  	char *p = s;
  
  	while ((p = strchr(p, from)) != NULL)
  		*p++ = to;
  
  	return s;
  }
  
  /**
   * ltrim - Removes leading whitespace from @s.
   * @s: The string to be stripped.
   *
   * Return pointer to the first non-whitespace character in @s.
   */
  char *ltrim(char *s)
  {
  	while (isspace(*s))
  		s++;
  
  	return s;
  }
  
  /**
   * rtrim - Removes trailing whitespace from @s.
   * @s: The string to be stripped.
   *
   * Note that the first trailing whitespace is replaced with a %NUL-terminator
   * in the given string @s. Returns @s.
   */
  char *rtrim(char *s)
  {
  	size_t size = strlen(s);
  	char *end;
  
  	if (!size)
  		return s;
  
  	end = s + size - 1;
  	while (end >= s && isspace(*end))
  		end--;
  	*(end + 1) = '\0';
  
  	return s;
  }
  
  char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
  {
  	/*
  	 * FIXME: replace this with an expression using log10() when we
  	 * find a suitable implementation, maybe the one in the dvb drivers...
  	 *
  	 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
  	 */
  	size_t size = nints * 28 + 1; /* \0 */
  	size_t i, printed = 0;
  	char *expr = malloc(size);
  
  	if (expr) {
  		const char *or_and = "||", *eq_neq = "==";
  		char *e = expr;
  
  		if (!in) {
  			or_and = "&&";
  			eq_neq = "!=";
  		}
  
  		for (i = 0; i < nints; ++i) {
  			if (printed == size)
  				goto out_err_overflow;
  
  			if (i > 0)
  				printed += scnprintf(e + printed, size - printed, " %s ", or_and);
  			printed += scnprintf(e + printed, size - printed,
  					     "%s %s %d", var, eq_neq, ints[i]);
  		}
  	}
  
  	return expr;
  
  out_err_overflow:
  	free(expr);
  	return NULL;
  }
  