/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 :
 * =====================================================================
 *  sc_string.c
 *  Copyright (c)  2003 - 2011  sys0tem
 *  LICENSE :
 *	LGPL (GNU Lesser General Public License - Version 3,29 June 2007)
 *	http://www.gnu.org/copyleft/lesser.html
 *	or
 *	EPL (Eclipse Public License - v1.0)
 *	http://www.eclipse.org/legal/epl-v10.html
 * =====================================================================
 */
#include <sc_os.h>

#if (SC_isWindows)
/* Windows の場合			*/
#include <malloc.h>

#else
/* Windows 以外は大抵 alloca.h に alloca がある				*/
#include <alloca.h>

#endif	/* if (isWindows)	*/

#include <sc_mmgr.h>
#include <sc_string.h>


/* ---------------------------------------------------------------------
 *  プロトタイプ宣言
 * ---------------------------------------------------------------------
 */
static bool SC_String_append(SC_String* str, const char* addstr);
static void SC_String_setLength(SC_String* str, size_t len);
static bool SC_String_isEmpty(SC_String* str);
static void SC_String_clear(SC_String* str);

/**
 * 指定された文字列 s の長さを返します。
 * 最大で max までの長さを返します。
 *
 * @param s 文字列
 * @param max 最大の長さ
 * @return 文字列の長さ
 */
size_t SC_strnlen(const char* s, size_t max)
{
	size_t count = 0;
	for (; count < max; count++)
	{
		if (s[count] == '\0')
		{
			break;
		}
	}
	return count;
}


/**
 * 指定された文字列 s の複製を作成し、
 * 新しい文字列へのポインタを返します。
 * 最大で n 文字だけ複製します。
 * メモリ確保に失敗した場合、NULL を返します。
 *
 * @param s 文字列
 * @param n 複製する最大文字数
 * @param file ソースファイル
 * @param line 行番号
 * @return 複製された文字列
 */
char* SC_strndup(const char* s, size_t n, const char* file, int line)
{
	size_t len;
	char*  newStr;
	if (s == NULL)
	{
		return NULL;
	}

	len = strnlen(s, n);
	if (file == NULL)
	{
		newStr = (char*) SC_realMalloc(len + 1);
	}
	else
	{
		newStr = (char*) SC_malloc(len + 1, file, line);
	}
	if (newStr == NULL)
	{	/* メモリ確保失敗	*/
		return NULL;
	}
	newStr[len] = '\0';
	return (char*) memcpy(newStr, s, len);
}


/**
 * 指定された文字列 s の複製を作成し、
 * 新しい文字列へのポインタを返します。
 * 最大で n 文字だけ複製します。
 * メモリ確保に失敗した場合、NULL を返します。
 * 本関数はメモリ確保に alloca を使用します。
 *
 * @param s 文字列
 * @param n 複製する最大文字数
 * @param file ソースファイル
 * @param line 行番号
 * @return 複製された文字列
 */
char* SC_strndupa(const char* s, size_t n)
{
	size_t len;
	char*  newStr;
	if (s == NULL)
	{
		return NULL;
	}
	len    = strnlen(s, n);
	newStr = (char*) alloca(len + 1);
	if (newStr == NULL)
	{	/* メモリ確保失敗	*/
		return NULL;
	}
	newStr[len] = '\0';
	return (char*) memcpy(newStr, s, len);
}


/**
 * 指定された target より、search に指定されたデータを検索し、
 * 見つかった位置のポインタを返します。
 * 見つからない場合 NULL を返します。
 * 検索対象が NULL の場合、NULL を返します。
 * 検索データが NULL の場合、target の先頭を返します。
 *
 * @param target 検索対象
 * @param tSize  検索対象サイズ
 * @param search 検索データ
 * @param sSize  検索データサイズ
 * @return 検索位置のポインタ
 */
const char* SC_memindex(const char* target, size_t tSize,
	const char* search, size_t sSize)
{
	int i;
	int ret;

	if (target == NULL)
	{	/* 検索対象が NULL なら NULL を返します。		*/
		return NULL;
	}

	if (search == NULL)
	{	/* 検索データが NULL なら target  の先頭を返す	*/
		return target;
	}

	for (i = (sSize - 1); i < tSize; i++)
	{
		if (target[i] == search[sSize - 1])
		{
			ret = memcmp(&target[i - sSize + 1], search, sSize);
			if (ret == 0)
			{
				return &(target[i - sSize + 1]);
			}
		}
	}
	return NULL;
}


/**
 * 文字列 str より、文字列 search がある位置を返します。
 * 見つからない場合は -1 を返します。
 *
 * @param str 文字列
 * @param search 検索文字
 * @return true/false (空文字/空文字でない)
 */
int SC_indexOf(const char* str, const char* search)
{
	char* posPtr = strstr(str, search);
	if (posPtr == NULL)
	{	/* 見つからない			*/
		return -1;
	}
	/* 見つかったので位置を返す	*/
	return (posPtr - str);
}


/**
 * 文字列 str より、文字列 search を末尾から検索し、
 * インデックスを返します。
 * 見つからない場合は -1 を返します。
 *
 * @param str 文字列
 * @param search 検索文字
 * @return true/false (空文字/空文字でない)
 */
int SC_lastIndexOf(const char* str, const char* search)
{
	int i;
	int check;
	int len       = strlen(str);
	int searchLen = strlen(search);

	if (searchLen == 0) { return -1; }
	for (i = (len - searchLen); i >= 0; i--)
	{
		if (str[i] == search[0])
		{
			check = strncmp(&str[i], search, searchLen);
			if (check == 0)
			{
				return i;
			}
		}
	}
	return -1;
}


/**
 * SC_String を生成します。
 *
 * @param bufSize 初期バッファサイズ
 * @return SC_String
 */
SC_String* SC_String_new(size_t bufSize)
{
	SC_String* str = (SC_String*) malloc(sizeof(SC_String));
	if (str == NULL)
	{	/* メモリ確保失敗	*/
		return NULL;
	}
	str->buffer = (char*) malloc(bufSize);
	if (str->buffer == NULL)
	{	/* メモリ確保失敗	*/
		free(str);
		return NULL;
	}

	str->_useSize = 0;
	str->_bufSize = bufSize;
	memset(str->buffer, '\0', str->_bufSize);

	str->append      = SC_String_append;
	str->setLength   = SC_String_setLength;
	str->isEmpty     = SC_String_isEmpty;
	str->clear       = SC_String_clear;
	return str;
}


/**
 * SC_String を破棄します。
 *
 * @param str 破棄する SC_String
 */
void SC_String_delete(SC_String* str)
{
	free(str->buffer);
	free(str);
}


/**
 * 指定された文字列を追加します。
 *
 * @param str    文字列
 * @param addstr 追加する文字列
 * @param true/false (追加成功/追加失敗)
 */
static
bool SC_String_append(SC_String* str, const char* addstr)
{
	size_t len      = strlen(addstr) + 1;
	size_t totalLen = str->_useSize + len;
	char*  tmpBuff;

	if (str->_bufSize < totalLen)
	{	/* バッファ拡張	*/
		tmpBuff = (char*) realloc(str->buffer, (totalLen << 1));
		if (tmpBuff == NULL)
		{	/* メモリ確保失敗	*/
			return false;
		}
		str->buffer  = tmpBuff;
		str->_bufSize = (totalLen << 1);
	}
	strcat(str->buffer, addstr);
	str->_useSize = totalLen - 1;
	return true;
}


/**
 * 文字列の長さを len に切り詰めます。
 * 文字列の長さが len 以下の場合は、なにもしません。
 *
 * @param str 文字列
 * @param len 切り詰める長さ
 */
static
void SC_String_setLength(SC_String* str, size_t len)
{
	if (len < str->_useSize)
	{
		str->buffer[len] = '\0';
		str->_useSize    = len;
	}
}


/**
 * 文字列が空文字かどうかを返します。
 *
 * @param str 文字列
 * @return true/false (空文字/空文字でない)
 */
static
bool SC_String_isEmpty(SC_String* str)
{
	if (str->_useSize == 0)
	{
		return true;
	}
	return false;
}


/**
 * 文字列をクリアします。
 * 確保済みのメモリは開放しません。
 * 確保済みのメモリも開放する場合は、SC_String_delete
 * を使用してください。
 *
 * @param str    文字列
 */
static
void SC_String_clear(SC_String* str)
{
	*(str->buffer) = '\0';
	str->_useSize  = 0;
}


