/****************************************************************************** * 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 #include #include #include #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; }