iSulad/src/cutils/utils_string.c
WangFengTu 4fabc9ffbf Support devicemapper
Signed-off-by: WangFengTu <wangfengtu@huawei.com>
2020-02-12 22:55:38 -05:00

735 lines
16 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: tanyifeng
* Create: 2018-11-1
* Description: provide container utils functions
*******************************************************************************/
#define _GNU_SOURCE
#include "utils_string.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "utils.h"
#include "log.h"
struct unit_map_def {
int64_t mltpl;
char *name;
};
static struct unit_map_def const g_unit_map[] = {
{.mltpl = 1, .name = "I"},
{.mltpl = 1, .name = "B"},
{.mltpl = 1, .name = "IB"},
{.mltpl = SIZE_KB, .name = "K"},
{.mltpl = SIZE_KB, .name = "KI"},
{.mltpl = SIZE_KB, .name = "KB"},
{.mltpl = SIZE_KB, .name = "KIB"},
{.mltpl = SIZE_MB, .name = "M"},
{.mltpl = SIZE_MB, .name = "MI"},
{.mltpl = SIZE_MB, .name = "MB"},
{.mltpl = SIZE_MB, .name = "MIB"},
{.mltpl = SIZE_GB, .name = "G"},
{.mltpl = SIZE_GB, .name = "GI"},
{.mltpl = SIZE_GB, .name = "GB"},
{.mltpl = SIZE_GB, .name = "GIB"},
{.mltpl = SIZE_TB, .name = "T"},
{.mltpl = SIZE_TB, .name = "TI"},
{.mltpl = SIZE_TB, .name = "TB"},
{.mltpl = SIZE_TB, .name = "TIB"},
{.mltpl = SIZE_PB, .name = "P"},
{.mltpl = SIZE_PB, .name = "PI"},
{.mltpl = SIZE_PB, .name = "PB"},
{.mltpl = SIZE_PB, .name = "PIB"}
};
static size_t const g_unit_map_len = sizeof(g_unit_map) / sizeof(g_unit_map[0]);
bool strings_contains_any(const char *str, const char *substr)
{
size_t i = 0;
size_t j;
size_t len_str = 0;
size_t len_substr = 0;
if (str == NULL || substr == NULL) {
return false;
}
len_str = strlen(str);
len_substr = strlen(substr);
for (i = 0; i < len_str; i++) {
for (j = 0; j < len_substr; j++) {
if (str[i] == substr[j]) {
return true;
}
}
}
return false;
}
int strings_count(const char *str, unsigned char c)
{
size_t i = 0;
int res = 0;
size_t len = 0;
if (str == NULL) {
return 0;
}
len = strlen(str);
for (i = 0; i < len; i++) {
if (str[i] == c) {
res++;
}
}
return res;
}
// strings_in_slice tests whether a string is contained in array of strings or not.
// Comparison is case insensitive
bool strings_in_slice(const char **strarray, size_t alen, const char *str)
{
size_t i;
if (strarray == NULL || alen == 0 || str == NULL) {
return false;
}
for (i = 0; i < alen; i++) {
if (strarray[i] != NULL && strcasecmp(strarray[i], str) == 0) {
return true;
}
}
return false;
}
// Returns a string that is generated after converting
// all uppercase characters in the str to lowercase.
char *strings_to_lower(const char *str)
{
char *newstr = NULL;
char *pos = NULL;
if (str == NULL) {
return NULL;
}
newstr = util_strdup_s(str);
if (newstr == NULL) {
return NULL;
}
for (pos = newstr; *pos; ++pos) {
*pos = (char)tolower((int)(*pos));
}
return newstr;
}
// Returns a string that is generated after converting
// all lowercase characters in the str to uppercase.
char *strings_to_upper(const char *str)
{
char *newstr = NULL;
char *pos = NULL;
if (str == NULL) {
return NULL;
}
newstr = util_strdup_s(str);
if (newstr == NULL) {
return NULL;
}
for (pos = newstr; *pos; ++pos) {
*pos = (char)toupper((int)(*pos));
}
return newstr;
}
static int parse_unit_multiple(const char *unit, int64_t *mltpl)
{
size_t i;
if (unit[0] == '\0') {
*mltpl = 1;
return 0;
}
for (i = 0; i < g_unit_map_len; i++) {
if (strcasecmp(unit, g_unit_map[i].name) == 0) {
*mltpl = g_unit_map[i].mltpl;
return 0;
}
}
return -EINVAL;
}
static int util_parse_size_int_and_float(const char *numstr, int64_t mlt, int64_t *converted)
{
long long int_size = 0;
double float_size = 0;
long long int_real = 0;
long long float_real = 0;
char *dot = NULL;
int nret;
dot = strchr(numstr, '.');
if (dot != NULL) {
char tmp;
// interger.float
if (dot == numstr || *(dot + 1) == '\0') {
return -EINVAL;
}
// replace 123.456 to 120.456
tmp = *(dot - 1);
*(dot - 1) = '0';
// parsing 0.456
nret = util_safe_strtod(dot - 1, &float_size);
// recover 120.456 to 123.456
*(dot - 1) = tmp;
if (nret < 0) {
return nret;
}
float_real = (int64_t)float_size;
if (mlt > 0) {
if (INT64_MAX / mlt < (int64_t)float_size) {
return -ERANGE;
}
float_real = (int64_t)(float_size * mlt);
}
*dot = '\0';
}
nret = util_safe_llong(numstr, &int_size);
if (nret < 0) {
return nret;
}
int_real = int_size;
if (mlt > 0) {
if (INT64_MAX / mlt < int_size) {
return -ERANGE;
}
int_real = int_size * mlt;
}
if (INT64_MAX - int_real < float_real) {
return -ERANGE;
}
*converted = int_real + float_real;
return 0;
}
int util_parse_byte_size_string(const char *s, int64_t *converted)
{
int ret;
int64_t mltpl = 0;
char *dup = NULL;
char *pmlt = NULL;
if (s == NULL || converted == NULL || s[0] == '\0' || !isdigit(s[0])) {
return -EINVAL;
}
dup = util_strdup_s(s);
if (dup == NULL) {
return -ENOMEM;
}
pmlt = dup;
while (*pmlt != '\0' && (isdigit(*pmlt) || *pmlt == '.')) {
pmlt++;
}
ret = parse_unit_multiple(pmlt, &mltpl);
if (ret) {
free(dup);
return ret;
}
// replace the first multiple arg to '\0'
*pmlt = '\0';
ret = util_parse_size_int_and_float(dup, mltpl, converted);
free(dup);
return ret;
}
int util_parse_percent_string(const char *s, long *converted)
{
char *dup = NULL;
if (s == NULL || converted == NULL || s[0] == 0 || strlen(s) < 2 || s[strlen(s) - 1] != '%') {
return -EINVAL;
}
dup = util_strdup_s(s);
if (dup == NULL) {
return -ENOMEM;
}
dup[strlen(dup) - 1] = 0;
*converted = strtol(dup, NULL, 10);
if ((errno == ERANGE && (*converted == LONG_MAX || *converted == LONG_MIN)) ||
(errno != 0 && *converted == 0) || *converted < 0 || *converted >= 100) {
free(dup);
return -EINVAL;
}
free(dup);
return 0;
}
static char **util_shrink_array(char **orig_array, size_t new_size)
{
char **new_array = NULL;
size_t i = 0;
if (new_size == 0) {
return orig_array;
}
if (new_size > SIZE_MAX / sizeof(char *)) {
ERROR("Invalid arguments");
return orig_array;
}
new_array = util_common_calloc_s(new_size * sizeof(char *));
if (new_array == NULL) {
return orig_array;
}
for (i = 0; i < new_size; i++) {
new_array[i] = orig_array[i];
}
free(orig_array);
return new_array;
}
static char **make_empty_array()
{
char **res_array = NULL;
res_array = calloc(2, sizeof(char *));
if (res_array == NULL) {
return NULL;
}
res_array[0] = util_strdup_s("");
return res_array;
}
char **util_string_split_multi(const char *src_str, char delim)
{
int ret, tmp_errno;
char *token = NULL;
char *cur = NULL;
char **res_array = NULL;
char deli[2] = { delim, '\0' };
size_t count = 0;
size_t capacity = 0;
char *tmpstr = NULL;
if (src_str == NULL) {
return NULL;
}
if (src_str[0] == '\0') {
return make_empty_array();
}
tmpstr = util_strdup_s(src_str);
cur = tmpstr;
token = strsep(&cur, deli);
while (token != NULL) {
ret = util_grow_array(&res_array, &capacity, count + 1, 16);
if (ret < 0) {
goto err_out;
}
res_array[count] = util_strdup_s(token);
count++;
token = strsep(&cur, deli);
}
free(tmpstr);
return util_shrink_array(res_array, count + 1);
err_out:
tmp_errno = errno;
free(tmpstr);
util_free_array(res_array);
errno = tmp_errno;
return NULL;
}
char **util_string_split(const char *src_str, char _sep)
{
char *token = NULL;
char *str = NULL;
char *tmpstr = NULL;
char *reserve_ptr = NULL;
char deli[2] = { _sep, '\0' };
char **res_array = NULL;
size_t capacity = 0;
size_t count = 0;
int ret, tmp_errno;
if (src_str == NULL) {
return NULL;
}
if (src_str[0] == '\0') {
return make_empty_array();
}
tmpstr = util_strdup_s(src_str);
str = tmpstr;
for (; (token = strtok_r(str, deli, &reserve_ptr)); str = NULL) {
ret = util_grow_array(&res_array, &capacity, count + 1, 16);
if (ret < 0) {
goto err_out;
}
res_array[count] = util_strdup_s(token);
count++;
}
if (res_array == NULL) {
free(tmpstr);
return make_empty_array();
}
free(tmpstr);
return util_shrink_array(res_array, count + 1);
err_out:
tmp_errno = errno;
free(tmpstr);
util_free_array(res_array);
errno = tmp_errno;
return NULL;
}
const char *str_skip_str(const char *str, const char *skip)
{
if (str == NULL || skip == NULL) {
return NULL;
}
for (; ; str++, skip++) {
if (*skip == 0) {
return str;
} else if (*str != *skip) {
return NULL;
}
}
}
static char *util_string_delchar_inplace(char *s, unsigned char c)
{
size_t i = 0;
size_t j = 0;
size_t slen = 0;
if (s == NULL) {
return NULL;
}
slen = strlen(s);
while (i < slen) {
if (j == slen) {
s[i] = '\0';
break;
}
s[i] = s[j];
if (s[i] != c) {
i++;
}
j++;
}
return s;
}
char *util_string_delchar(const char *ss, unsigned char c)
{
char *s = NULL;
if (ss == NULL) {
return NULL;
}
s = util_strdup_s(ss);
return util_string_delchar_inplace(s, c);
}
void util_trim_newline(char *s)
{
size_t len;
if (s == NULL) {
return;
}
len = strlen(s);
while ((len >= 1) && (s[len - 1] == '\n')) {
s[--len] = '\0';
}
}
static char *util_left_trim_space(char *str)
{
char *begin = str;
char *tmp = str;
while (isspace(*begin)) {
begin++;
}
while ((*tmp++ = *begin++)) {}
return str;
}
static char *util_right_trim_space(char *str)
{
char *end = NULL;
size_t len = strlen(str);
if (len == 0) {
return str;
}
end = str + len - 1;
while (isspace(*end)) {
end--;
}
*(end + 1) = '\0';
return str;
}
char *util_trim_space(char *str)
{
if (str == NULL) {
return NULL;
}
str = util_left_trim_space(str);
str = util_right_trim_space(str);
return str;
}
static char *util_left_trim_quotation(char *str)
{
char *begin = str;
char *tmp = str;
if (*str == '\0') {
return str;
}
while ((*begin) == '\"') {
begin++;
}
while ((*tmp++ = *begin++)) {}
return str;
}
static char *util_right_trim_quotation(char *str)
{
char *end = NULL;
size_t len = strlen(str);
if (len == 0) {
return str;
}
end = str + len - 1;
while (end >= str && ((*end) == '\0' || (*end) == '\n' || (*end) == '\"')) {
end--;
}
*(end + 1) = '\0';
return str;
}
char *util_trim_quotation(char *str)
{
if (str == NULL) {
return NULL;
}
str = util_left_trim_quotation(str);
str = util_right_trim_quotation(str);
return str;
}
char **str_array_dup(const char **src, size_t len)
{
size_t i;
char **dest = NULL;
if (len == 0 || src == NULL) {
return NULL;
}
if (len > SIZE_MAX / sizeof(char *) - 1) {
return NULL;
}
dest = (char **)util_common_calloc_s(sizeof(char *) * (len + 1));
if (dest == NULL) {
return NULL;
}
for (i = 0; i < len; ++i) {
if (src[i] != NULL) {
dest[i] = util_strdup_s(src[i]);
}
}
return dest;
}
static char *do_string_join(const char *sep, const char **parts, size_t parts_len, size_t result_len)
{
char *res_string = NULL;
size_t iter;
res_string = calloc(result_len + 1, 1);
if (res_string == NULL) {
return NULL;
}
for (iter = 0; iter < parts_len - 1; iter++) {
(void)strcat(res_string, parts[iter]);
(void)strcat(res_string, sep);
}
(void)strcat(res_string, parts[parts_len - 1]);
return res_string;
}
char *util_string_join(const char *sep, const char **parts, size_t len)
{
size_t sep_len;
size_t result_len;
size_t iter;
if (len == 0 || parts == NULL || sep == NULL) {
return NULL;
}
sep_len = strlen(sep);
if ((sep_len != 0) && (sep_len != 1) && (len > SIZE_MAX / sep_len + 1)) {
return NULL;
}
result_len = (len - 1) * sep_len;
for (iter = 0; iter < len; iter++) {
if (parts[iter] == NULL || result_len >= SIZE_MAX - strlen(parts[iter])) {
return NULL;
}
result_len += strlen(parts[iter]);
}
return do_string_join(sep, parts, len, result_len);
}
char *util_string_append(const char *post, const char *pre)
{
char *res_string = NULL;
size_t length = 0;
if (post == NULL && pre == NULL) {
return NULL;
}
if (pre == NULL) {
return util_strdup_s(post);
}
if (post == NULL) {
return util_strdup_s(pre);
}
if (strlen(post) > ((SIZE_MAX - strlen(pre)) - 1)) {
ERROR("String is too long to be appended");
return NULL;
}
length = strlen(post) + strlen(pre) + 1;
res_string = util_common_calloc_s(length);
if (res_string == NULL) {
return NULL;
}
(void)strcat(res_string, pre);
(void)strcat(res_string, post);
return res_string;
}
int dup_array_of_strings(const char **src, size_t src_len, char ***dst, size_t *dst_len)
{
size_t i;
if (src == NULL || src_len == 0) {
return 0;
}
if (dst == NULL || dst_len == NULL) {
return -1;
}
*dst = NULL;
*dst_len = 0;
if (src_len > SIZE_MAX / sizeof(char *)) {
ERROR("Src elements is too much!");
return -1;
}
*dst = (char **)util_common_calloc_s(src_len * sizeof(char *));
if (*dst == NULL) {
ERROR("Out of memory");
return -1;
}
for (i = 0; i < src_len; i++) {
(*dst)[*dst_len] = (src[i] != NULL) ? util_strdup_s(src[i]) : NULL;
(*dst_len)++;
}
return 0;
}
char *util_sub_string(const char *source, size_t offset, size_t length)
{
size_t total_len;
size_t substr_len;
char *substring = NULL;
if (source == NULL || length == 0) {
return NULL;
}
total_len = strlen(source);
substr_len = ((total_len - offset) >= length ? length : (total_len - offset)) + 1;
substring = (char *)util_common_calloc_s(substr_len * sizeof(char));
if (substring == NULL) {
ERROR("Out of memory\n");
return NULL;
}
(void)strncpy(substring, source + offset, substr_len - 1);
substring[substr_len - 1] = '\0';
return substring;
}
bool util_is_space_string(const char *str)
{
size_t i;
if (str == NULL) {
return false;
}
for (i = 0; i < strlen(str); i++) {
if (!isspace(str[i])) {
return false;
}
}
return true;
}