doubango/tinySAK/src/tsk_string.c
c732d49e
 
 /*
74ca6d11
 * Copyright (C) 2020, University of the Basque Country (UPV/EHU)
c732d49e
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://doubango.org>
 *
 * This file is part of Open Source Doubango Framework.
 *
 * DOUBANGO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DOUBANGO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with DOUBANGO.
 *
 */
 
 /**@file tsk_string.c
  * @brief Useful string functions to manipulate strings.
  * As I'm a lazy man, some comments come from <ahref="http://www.cplusplus.com">this website</a>
  */
 #include "tsk_string.h"
 #include "tsk_memory.h"
 #include "tsk_time.h"
 #include "tsk_debug.h"
 
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
 
 #if TSK_UNDER_WINDOWS_RT
 #include <Windows.h> /* MultiByteToWideChar */
 #include <vector>
 #endif
 
 
 #if defined(_MSC_VER)
 #	define snprintf		_snprintf
 #	define vsnprintf	_vsnprintf
 #	define strdup		_strdup
 #	define stricmp		_stricmp
 #	define strnicmp		_strnicmp
 #else
 #	if !HAVE_STRNICMP && !HAVE_STRICMP
 #	define stricmp		strcasecmp
 #	define strnicmp		strncasecmp
 #	endif
 #endif
 
 /**@defgroup tsk_string_group String utillity functions.
 */
 
 /**@ingroup tsk_string_group
 */
 tsk_string_t* tsk_string_create(const char* str)
 {
 	return (tsk_string_t*)tsk_object_new(tsk_string_def_t, str);
 }
 
 int tsk_string_pred_icmp(const tsk_list_item_t* item, const void* str)
 {
 	if (item && str){
 		return tsk_stricmp(TSK_STRING_STR(item->data), (const char*)str);
 	}
 	return -1;
 }
 
 int tsk_string_pred_cmp(const tsk_list_item_t* item, const void* str)
 {
 	if (item && str){
 		return tsk_strcmp(TSK_STRING_STR(item->data), (const char*)str);
 	}
 	return -1;
 }
 
 /**@ingroup tsk_string_group
 * From base 10 to base 16
 * @param c the base 10 char to convert to base 16
 * @retval The base 16 value
 */
 char tsk_b10tob16(char c)
 {
 	static char HEX[] = "0123456789abcdef";
 	return HEX[c & 15];
 }
 
 /**@ingroup tsk_string_group
 * From base 16 to base 10
 * @param c The base 16 char to convert to base 10
 * @retval The base 10 value
 */
 char tsk_b16tob10(char c)
 {
 	return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10;
 }
 
 /**@ingroup tsk_string_group
 * Compare two Null-terminated strings (case insensitive)
 * Compares the C string str1 to the C string str2.
 * This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs
 * until the characters differ or until a terminanting null-character is reached.
 * @param str1 First C string to be compared.
 * @param str2 Second C string to be compared.
 * @retval Returns an integral value indicating the relationship between the strings:
 * <0 : str1 less than str2.<br>
 * 0  : str1 identical to str2.<br>
 * >0 : str1 greater than str2.<br>
 */
 int tsk_stricmp(const char * str1, const char * str2)
 {
 	return (str1 && str2) ?
 		((tolower(*str1) == tolower(*str2)) ? stricmp(str1, str2) : (*str1 - *str2))  /* Compare first charaters before doing complete comparison */
 		:
 		((!str1 && !str2) ? 0 : -1);
 }
 
 /**@ingroup tsk_string_group
 * Compare two Null-terminated strings (case insensitive)
 * Compares the C string str1 to the C string str2.
 * This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs
 * until the characters differ or until a terminanting null-character is reached or @a n characters passed.
 * @param str1 First C string to be compared.
 * @param str2 Second C string to be compared.
 * @param n The maximum number of characters to compare.
 * @retval Returns an integral value indicating the relationship between the strings:
 * <0 : str1 less than str2.<br>
 * 0  : str1 identical to str2.<br>
 * >0 : str1 greater than str2.<br>
 */
 int tsk_strnicmp(const char * str1, const char * str2, tsk_size_t n)
 {
 	return (str1 && str2 && n) ?
 		((tolower(*str1) == tolower(*str2)) ? strnicmp(str1, str2, n) : (*str1 - *str2))  /* Compare first charaters before doing complete comparison */
 		:
 		((!str1 && !str2) ? 0 : -1);
 }
 
 /**@ingroup tsk_string_group
 */
 int tsk_strcmp(const char * str1, const char * str2)
 {
 	return (str1 && str2) ?
 		((*str1 == *str2) ? stricmp(str1, str2) : (*str1 - *str2))  /* Compare first charaters before doing complete comparison */
 		:
 		((!str1 && !str2) ? 0 : -1);
 }
 
 /**@ingroup tsk_string_group
 * Compare two Null-terminated strings (case sensitive)
 * Compares the C string str1 to the C string str2.
 * This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs
 * until the characters differ or until a terminanting null-character is reached.
 * @param str1 First C string to be compared.
 * @param str2 Second C string to be compared.
 * @param n The maximum number of characters to compare.
 * @retval Returns an integral value indicating the relationship between the strings:
 * <0 : str1 less than str2.<br>
 * 0  : str1 identical to str2.<br>
 * >0 : str1 greater than str2.<br>
 */
 int tsk_strncmp(const char * str1, const char * str2, tsk_size_t n)
 {
 	return (str1 && str2) ? ((*str1 != *str2) ? -1 : strncmp(str1, str2, n)) : ((!str1 && !str2) ? 0 : -1);
 }
 
 /**@ingroup tsk_string_group
 * Duplicate a Null-terminated string.
 * @param s1 The string to duplicate.
 * @retval The duplicated string. It's up to you to free the returned string.
 */
 char* tsk_strdup(const char *s1)
 {
 	if (s1){
 		return strdup(s1);
 	}
 	return tsk_null;
 }
 
 /**	Duplicates the first @a n chars of @a s1.
  * @param s1 The string to duplicate.
  * @param n The number of characters to copy to the new string.
  * @retval	null A copy of @a s1.
  **/
 char* tsk_strndup(const char *s1, tsk_size_t n)
 {
 	char *ret = tsk_null;
 
 	if (s1 && n){
 		tsk_size_t len = tsk_strlen(s1);
 		tsk_size_t nret = (n > len) ? (len) : (n);
 
 		#if HAVE_CRT //Debug memory
 		if ((ret = (char*)calloc((nret + 1), sizeof(uint8_t)))){
 		
 	#else
 		if ((ret = (char*)tsk_calloc((nret + 1), sizeof(uint8_t)))){
 		
 	#endif //HAVE_CRT
 			memset(ret,0,(nret+1)*sizeof(uint8_t));
 			memcpy(ret, s1, nret);
 		}
 	}
 
 	return ret;
 }
 
 /**@ingroup tsk_string_group
 * Checks if @a str contains @a substring.
 * @param str The master string.
 * @param size The size of the master string.
 * @param substring the substring.
 * @retval @a tsk_true if @a str contains at least one occurence of @a substring and @a tsk_false othewise.
 */
 tsk_bool_t tsk_strcontains(const char * str, tsk_size_t size, const char * substring)
 {
 	return (tsk_strindexOf(str, size, substring) >= 0);
 }
 
 /**@ingroup tsk_string_group
 * Gets the first occurrence of @a substring within @a str.
 * @param str The master string.
 * @param size The size of the master string.
 * @param substring The substring that is to be searched for within @a str.
 * @retval The index of the first ocurrence of @a substring in @a str.
 * If no occurrence of @a substring is found, then -1 is returned.
 */
 int tsk_strindexOf(const char * str, tsk_size_t size, const char * substring)
 {
 	if (str && substring){
175b478c
 		//const char* sub_start = strstr(str, substring);
 		tsk_size_t sub_size = tsk_strlen(substring);
 		const char* sub_start = memmem(str, size, substring, sub_size);
c732d49e
 		if (sub_start && (sub_start < (str + size))) {
 			int ret;
 			tsk_subsat_int32_ptr(sub_start, str, &ret);
 			return ret;
 		}
 	}
 	return -1;
 }
 
 /**@ingroup tsk_string_group
 */
 int tsk_strLastIndexOf(const char * str, tsk_size_t size, const char * substring)
 {
 	if (str && substring){
 		tsk_size_t sub_size = tsk_strlen(substring);
 		const char* last_sub_start = tsk_null;
175b478c
         //const char* sub_start = strstr(str, substring);
         const char* sub_start = memmem(str, size, substring, sub_size);
c732d49e
 		const char* end = (str + size);
 		while (sub_start && (sub_start < end)) {
 			last_sub_start = sub_start;
175b478c
 			if ((sub_start + sub_size) < (end-sub_size)){
                 //sub_start = strstr((sub_start + sub_size), substring);
                 sub_start = memmem((sub_start + sub_size), (end - (sub_start + sub_size)), substring, sub_size);
c732d49e
 			}
 			else {
 				break;
 			}
 		}
 		if (last_sub_start) {
 			int ret;
 			tsk_subsat_int32_ptr(last_sub_start, str, &ret);
 			return ret;
 		}
 	}
 	return -1;
 }
 
 /**@ingroup tsk_string_group
 * Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source,
 * and a new null-character is appended at the end of the new string formed by the concatenation of both in destination. If the destination is NULL then new
 * memory will allocated and filled with source value.
 * @param destination Pointer de the destination array containing the new string.
 * @param source C string to be appended. This should not overlap destination. If NULL then nothing is done.
 */
 void tsk_strcat(char** destination, const char* source)
 {
 	tsk_strncat(destination, source, tsk_strlen(source));
 }
 
 /**@ingroup tsk_string_group
 */
 void tsk_strcat_2(char** destination, const char* format, ...)
 {
 	char* temp = tsk_null;
 	int len;
 	va_list ap;
 
 	/* initialize variable arguments */
 	va_start(ap, format);
 	/* compute */
 	if ((len = tsk_sprintf_2(&temp, format, &ap))){
 		tsk_strncat(destination, temp, len);
 	}
 	/* reset variable arguments */
 	va_end(ap);
 	TSK_FREE(temp);
 }
 
 /**@ingroup tsk_string_group
 */
 void tsk_strncat(char** destination, const char* source, tsk_size_t n)
 {
 	tsk_size_t index = 0;
 	tsk_size_t tsk_size_to_cat = (n > tsk_strlen(source)) ? tsk_strlen(source) : n;
 
 	if (!source || !n) {
 		return;
 	}
 
 	if (!*destination) {
 		#if HAVE_CRT //Debug memory
 		*destination = (char*)malloc(tsk_size_to_cat + 1);
 		#else
 		*destination = (char*)tsk_malloc(tsk_size_to_cat + 1);
 		#endif //HAVE_CRT
 		strncpy(*destination, source, tsk_size_to_cat + 1);
 	}
 	else {
 		index = tsk_strlen(*destination);
 		*destination = (char*)tsk_realloc(*destination, index + tsk_size_to_cat + 1);
 		strncpy(((*destination) + index), source, tsk_size_to_cat + 1);
 	}
 	(*destination)[index + tsk_size_to_cat] = '\0';
 }
 
 /**@ingroup tsk_string_group
 * Writes into the array pointed by str a C string consisting on a sequence of data formatted as the format argument specifies. After the format parameter,
 * the function expects at least as many additional arguments as specified in format.
 * This function behaves exactly as printf does, but writing its results to a string instead of stdout. The size of the array passed as str should be enough to
 * contain the entire formatted string.
 * @param str Pointer to an array of char elements where the resulting C string is stored.
 * MUST be NULL.
 * @param format C string that contains the text to be written to the buffer. For more information see definiton of C function @a sprintf
 * @retval On success, the total number of characters written is returned. This count does not include the additional null-character automatically appended
 * at the end of the string.
 * On failure, a negative number is returned.
 */
 int tsk_sprintf(char** str, const char* format, ...)
 {
 	int len;
 	va_list ap;
 
 	/* initialize variable arguments */
 	va_start(ap, format);
 	/* compute */
 	len = tsk_sprintf_2(str, format, &ap);
 	/* reset variable arguments */
 	va_end(ap);
 
 	return len;
 }
 
 /**@ingroup tsk_string_group
 */
 int tsk_sprintf_2(char** str, const char* format, va_list* ap,...)
 {
 	int len = 0;
 	va_list ap2;
 
 	/* free previous value */
 	if (*str){
 		tsk_free((void**)str);
 	}
 
 	/* needed for 64bit platforms where vsnprintf will change the va_list */
 	va_start(ap2,"string data list");
 	tsk_va_copy(ap2, *ap);
 	
 
 	/* compute destination len for windows mobile
 	*/
 #if defined(_WIN32_WCE)
 	{
 		int n;
 		len = (tsk_strlen(format) * 2);
 		#if HAVE_CRT //Debug memory
 		*str = (char*)calloc(1, len+1);
 		
 	#else
 		*str = (char*)tsk_calloc(1, len+1);
 		
 	#endif //HAVE_CRT
 		for(;;){
 			if( (n = vsnprintf(*str, len, format, *ap)) >= 0 && (n<len) ){
 				len = n;
 				goto done;
 			}
 			else{
 				len += 10;
 				*str = (char*)tsk_realloc(*str, len+1);
 			}
 		}
 	done:
 		(*str)[len] = '\0';
 	}
 #else
 	len = vsnprintf(0, 0, format, *ap);
 	#if HAVE_CRT //Debug memory
 		*str = (char*)calloc(1, len + 1);
 	
 	#else
 		*str = (char*)tsk_calloc(1, len + 1);
 	
 	#endif //HAVE_CRT
 	vsnprintf(*str, len
 #if !defined(_MSC_VER) || defined(__GNUC__)
 		+1
 #endif
 		, format, ap2);
 #endif
 
 	va_end(ap2);
 
 	return len;
 	}
 
 /**@ingroup tsk_string_group
 * Updates the value of @a str.
 * @param str The string to update.
 * @param newval The new value of @a str.
 */
 void tsk_strupdate(char** str, const char* newval)
 {
 	if (str && *str != newval){ // do nothing if same memory address
 		// use realloc() to keep same memory address
 		tsk_size_t length = tsk_strlen(newval);
 		if (!length) {
 			tsk_free((void**)str);
 		}
 		else if ((*str = (char*)tsk_realloc(*str, length + 1))){
 			memcpy(*str, newval, length);
 			(*str)[length] = '\0';
 		}
 	}
 }
 
 
 /**@ingroup tsk_string_group
 * Removes all occurrences of white space characters from the beginning of this @a str.
 * @param str The string to trim.
 */
 void tsk_strtrim_left(char **str)
 {
 	if (str && *str) {
 		tsk_size_t count = 0;
 		while (isspace(*((*str) + count))) count++;
 		if (count) {
 			tsk_size_t len = tsk_strlen((*str));
 			memmove((*str), (*str) + count, (len - count));
 			(*str)[len - count] = '\0';
 		}
 	}
 }
 
 /**@ingroup tsk_string_group
 * Removes all occurrences of white space characters from the end of @a str.
 * @param str The string to trim.
 */
 void tsk_strtrim_right(char **str)
 {
 	if (str && *str){
 		tsk_size_t size;
 		if ((size = tsk_strlen(*str))){
 			while (isspace(*((*str) + size - 1))) size--;
 			*(*str + size) = '\0';
 		}
 	}
 }
 /**@ingroup tsk_string_group
 * Removes all occurrences of white space characters from the beginning and end of @a str.
 * @param str The string to trim.
 */
 void tsk_strtrim(char **str)
 {
 	// left
 	tsk_strtrim_left(str);
 	// right
 	tsk_strtrim_right(str);
 }
 
 /**@ingroup tsk_string_group
 * Adds quotes ("") to the beginning and end of @a str.<br>
 * @param str The string to quote.
 * Example: tsk_strquote("doubango") = ""doubango\"".
 */
 void tsk_strquote(char **str)
 {
 	tsk_strquote_2(str, '"', '"');
 }
 
 /**@ingroup tsk_string_group
 * Adds quotes to the beginning and end of @a str.
 * @param str The string to quote.
 * @param lquote Quote to add to the begining of @a str.
 * @param rquote Quote to add to the end of @a str.
 */
 void tsk_strquote_2(char **str, char lquote, char rquote)
 {
 	if (str && *str){
 		char *result = tsk_null;
 		tsk_sprintf(&result, "%c%s%c", lquote, *str, rquote);
 		tsk_free((void**)str);
 		*str = result;
 	}
 }
 
 /**@ingroup tsk_string_group
 * Removes quotes ("") from the beginning and end of @a str.<br>
 * @param str The string to unquote.
 * Example: tsk_strunquote(""doubango"") = "doubango".
 */
 void tsk_strunquote(char **str)
 {
 	tsk_strunquote_2(str, '"', '"');
 }
 
 /**@ingroup tsk_string_group
 * Removes quotes from the beginning and end of @a str. The string must starts with @a lquote
 * and end with @a rquote.
 * @param str The string to unquote.
 * @param lquote Quote to remove from the begining of @a str.
 * @param rquote Quote to remove from the end of @a str.
 */
 void tsk_strunquote_2(char **str, char lquote, char rquote)
 {
 	if (str && *str) {
 		tsk_size_t size = tsk_strlen(*str);
 		if (size >= 2 && **str == lquote && *((*str) + size - 1) == rquote){
 			memmove((*str), (*str) + 1, (size - 2));
 			*((*str) + size - 2) = '\0';
 		}
 	}
 }
 
 /**@ingroup tsk_string_group
 * Conversts an integer to string.
 * @param i The integer number to convert to a string.
 * @param result Pointer to the string where to copy the result.
 */
 void tsk_itoa(int64_t i, tsk_istr_t *result)
 {
 	memset(result, 0, sizeof(*result));
 	sprintf(*result, "%lld", i);
 }
 
 /**@ingroup tsk_string_group
 */
 int64_t tsk_atoll(const char* str)
 {
 	// FIXME: use HAVE_ATOLL and use macro instead of function
 	if (str){
 #if defined(_MSC_VER)
 		return _atoi64(str);
 #elif defined(__GNUC__)
 		return atoll(str);
 #else
 		return atol(str);
 #endif
 	}
 	return 0;
 }
 
 /**@ingroup tsk_string_group
 */
 long tsk_atox(const char* str)
 {
 	long ret = 0;
 	if (str){
 		sscanf(str, "%lx", &ret);
 	}
 	return ret;
 }
 
 /**@ingroup tsk_string_group
  * Generates a random string.
  *
  * @param result	A pointer to the result.
  **/
 void tsk_strrandom(tsk_istr_t *result)
 {
 	static uint64_t __counter = 1;
 	tsk_itoa((tsk_time_now() ^ (rand())) ^ ++__counter, result);
 }
 
 /**@ingroup tsk_string_group
  *
  * Converts hexadecimal bytes into string representation.
  *
  * @param hex	The hexadecimal bytes to convert.
  * @param	size		The size of the hexadecimal bytes.
  * @param str	The pointer to the result. MUST be enought large to hold the result.
  *						It is up to you to add the final '\\0'.
  * @sa @ref tsk_str_to_hex
  **/
 void tsk_str_from_hex(const uint8_t *hex, tsk_size_t size, char* str)
 {
 	static const char *TSK_HEXA_VALUES = { "0123456789abcdef" };
 	tsk_size_t i;
 
 	for (i = 0; i < size; i++){
 		str[2 * i] = TSK_HEXA_VALUES[(*(hex + i) & 0xf0) >> 4];
 		str[(2 * i) + 1] = TSK_HEXA_VALUES[(*(hex + i) & 0x0f)];
 	}
 }
 
 
 /**@ingroup tsk_string_group
  * Converts string chars into hexadecimal bytes.
  *
  * @param str	If non-null, the string.
  * @param	size		The size.
  * @param hex	If non-null, the hexadecimal.
  **/
 void tsk_str_to_hex(const char *str, tsk_size_t size, uint8_t* hex)
 {
 	// to avoid SIGBUS error when memory is misaligned do not use sscanf("%2x")
 	//TSK_DEBUG_FATAL("Not implemented.");
 	tsk_size_t i;
 	tsk_size_t hex_size;
 
 	if (size % 2 != 0)
 		return;
 
 	hex_size = size / 2;
 	for (i = 0; i < size; i += 2) {
 		hex[i/2] = (((tsk_hex_to_int(str[i]) & 0x0F) << 4) | (tsk_hex_to_int(str[i + 1]) & 0x0F));
 	}
 }
 
 uint8_t tsk_hex_to_int(char c)
 {
 	if (c >= '0' && c <= '9') return      c - '0';
 	if (c >= 'A' && c <= 'F') return 10 + c - 'A';
 	if (c >= 'a' && c <= 'f') return 10 + c - 'a';
 	return 0;
 }
 
 
 
 #if TSK_UNDER_WINDOWS_RT
 
 TINYSAK_API std::vector<char> rt_tsk_str_to_native(Platform::String^ str)
 {
 	if(str != nullptr && !str->IsEmpty())
 	{
 		int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str->Data(), str->Length(), nullptr, 0, nullptr, nullptr);
 		if (len > 0)
 		{
 			std::vector<char> vec(len + 1);
 			if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str->Data(), str->Length(), vec.data(), len, nullptr, nullptr) == len)
 			{
 				return std::move(vec);
 			}
 		}
 	}
 	return std::move(std::vector<char>(0));
 }
 
 TINYSAK_API Platform::String^  rt_tsk_str_to_managed(char const* str)
 {
 	if(str)
 	{
 		int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nullptr, 0);
 		if (len > 0)
 		{
 			std::vector<wchar_t> vec(len);
 			if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, vec.data(), len) == len)
 			{
 				return ref new Platform::String(vec.data());
 			}
 		}
 	}
 	return nullptr;
 }
 
 #endif /* TSK_UNDER_WINDOWS_RT */
 
 
 
 
 
 
 //=================================================================================================
 //	String object definition
 //
 static tsk_object_t* tsk_string_ctor(tsk_object_t * self, va_list * app)
 {
 	tsk_string_t *string = (tsk_string_t*)self;
 	const char *value = va_arg(*app, const char *);
 	if (value){
 		string->value = tsk_strdup(value);
 	}
 	return self;
 }
 
 static tsk_object_t* tsk_string_dtor(tsk_object_t * self)
 {
 	tsk_string_t *string = (tsk_string_t*)self;
 	if (string){
 		TSK_FREE(string->value);
 	}
 
 	return self;
 }
 
 static int tsk_string_cmp(const tsk_object_t *_s1, const tsk_object_t *_s2)
 {
 	const tsk_string_t *s1 = (const tsk_string_t *)_s1;
 	const tsk_string_t *s2 = (const tsk_string_t *)_s2;
 
 	if (s1 && s2){
 		return tsk_stricmp(s1->value, s2->value);
 	}
 	else if (!s1 && !s2) return 0;
 	else return -1;
 }
 
 static const tsk_object_def_t tsk_string_def_s =
 {
 	sizeof(tsk_string_t),
 	tsk_string_ctor,
 	tsk_string_dtor,
 	tsk_string_cmp,
 };
 const tsk_object_def_t *tsk_string_def_t = &tsk_string_def_s;