/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

	【機能概要】	: C++言語関連で使用する共通関数群
					  本アプリで実装している共通関数は、以下の通り
					  使用方法については、各関数の実装部で記述

	【作成日】		: 2021.04.23

	【実装関数】	: getLocalTimeString		# 現在時刻の取得と指定フォーマット化
					: strnumber					# int型数値を文字列に変換
					: ExpandEnvironmentStrings	# 文字列中から環境変数定義部を展開して結果を返却
					: getEnvString				# 環境変数char型返却(初期値設定付き)
					: getEnvLong				# 環境変数Long型返却(初期値設定付き)
					: strtokEX					# strtokの拡張版、["],['],[()]による区切り保護が可能
					: strTrim					# 文字列の前後半角スペースをトリムする
					: strLTrim					# 文字列の前部半角スペースをトリムする
					: startLock					# 排他ロックを開始する
					: endLock					# 排他ロックを解除する
					: initLock					# 排他ロックを初期化する

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */

#include "compatible_function.h"

#ifndef WIN32
pthread_mutex_t	mutex [ maxMutexLock ] ;		// mutexロック(unix)
#else
HANDLE	hMutex [ maxMutexLock ] ;				// mutexロック(win・但し、windowsは未実装箇所あり)
#endif

int mutexCount = 0 ;							// mutexロック数

//*******************************************************************************
//* getLocalTimeString関数														*
//*******************************************************************************
//* 現在のローカル時刻を取得し指定フォーマットにあわせて文字列に変換する
//* parameter	O	cOutputString		:出力バッファ
//*				I	maxStringSize		:出力バッファ長
//*				I	cFormatString		:書式
//* return		0以上					:文字列変換成功(文字列長)
//*				0						:文字列変換失敗
//*				-1						:時刻取得失敗
//* 備考:使用できるフォーマット指定コードは以下の通り
//*       %Y    :10進数で表す4桁の西暦 
//*       %y    :10進数で表す西暦の下 2 桁 (00〜99) 
//*       %m    :10進数で表す月 (01〜12) 
//*       %d    :10進数で表す月の日付 (01〜31) 
//*       %p    :現在のロケールの am/pm (小文字)
//*       %P    :現在のロケールの AM/PM (大文字)
//*       %H    :24時間表記の時間 (00〜23) 
//*       %h    :12時間表記の時間 (00〜11) 
//*       %M    :10進数で表す分 (00〜59) 
//*       %S    :10進数で表す秒 (00〜59) 
//*       %u    :10進数で表すミリ秒(000〜999)
//*       %U    :10進数で表すマイクロ秒(000000〜999999)
//*       %%    :%記号

long	getLocalTimeString(
	char		*	cOutputString,
	size_t			maxStringSize,
	const char	*	cFormatString)
{
//変数定義
#ifdef WIN32
	struct timeb	tp;			//システム時刻格納用
#else
	struct timeval	tp;			//システム時刻格納用
#endif
	struct tm		*tmc;		//時刻情報取得用
	struct tm		tmc_r;		//時刻情報取得用(実体)
	long			iMicroSec;	//マイクロ秒格納
	size_t			szLeft;		//未使用の出力バッファ長
	int				iTmp;

//
//現在時刻取得
//
#ifdef WIN32
	ftime(&tp);
	tmc = localtime_r ( &tp.time , &tmc_r );
	iMicroSec = tp.millitm*1000;
#else
	(void)gettimeofday( &tp,NULL );
	tmc = localtime_r ( &tp.tv_sec , &tmc_r);
	iMicroSec = tp.tv_usec;
#endif
	if(tmc == NULL){
		return -1;
	}

//
//現在時刻文字列変換
//
	szLeft = maxStringSize;	//未使用出力バッファ長に引数の出力バッファ長を入れて初期化
	while (szLeft > 0) {
		switch (*cFormatString){
			case '\0':	//書式終了時は文字列変換終了
				goto done;
			case '%':	//書式の先頭文字の場合は書式にあわせて文字列変換
				cFormatString++;
				switch (*cFormatString){
					case 'd':
						strnumber(tmc->tm_mday,2,&cOutputString,&szLeft);
						break;
					case 'H':
						strnumber(tmc->tm_hour,2,&cOutputString,&szLeft);
						break;
					case 'h':
						if(tmc->tm_hour < 12){
							strnumber(tmc->tm_hour,2,&cOutputString,&szLeft);
						}
						else{
							strnumber(tmc->tm_hour-12,2,&cOutputString,&szLeft);
						}
						break;
					case 'p':
						if(tmc->tm_hour < 12){
							* cOutputString      = 'a';
							*(cOutputString + 1) = 'm';
						}
						else{
							* cOutputString      = 'p';
							*(cOutputString + 1) = 'm';
						}
						cOutputString += 2;
						break;
					case 'P':
						if(tmc->tm_hour < 12){
							* cOutputString      = 'A';
							*(cOutputString + 1) = 'M';
						}
						else{
							* cOutputString      = 'P';
							*(cOutputString + 1) = 'M';
						}
						cOutputString += 2;
						szLeft -= 2;
						break;
					case 'm':
						strnumber(tmc->tm_mon+1,2,&cOutputString,&szLeft);
						break;
					case 'M':
						strnumber(tmc->tm_min,2,&cOutputString,&szLeft);
						break;
					case 'S':
						strnumber(tmc->tm_sec,2,&cOutputString,&szLeft);
						break;
					case 'U':
						strnumber(iMicroSec,6,&cOutputString,&szLeft);
						break;
					case 'u':
						strnumber(iMicroSec / 1000,3,&cOutputString,&szLeft);
						break;
					case 'y':
						strnumber(tmc->tm_year % 100,2,&cOutputString,&szLeft);
						break;
					case 'Y':
						iTmp = (tmc->tm_year / 100 + 19) * 100 + tmc->tm_year % 100;
						strnumber(iTmp,4,&cOutputString,&szLeft);
						break;
					case '%':
						*cOutputString = '%';
						cOutputString++;
						szLeft--;
						break;
					default :
						break;
				}
				cFormatString++;
				break;
			default:	//その他の文字はそのまま出力
				*cOutputString = *cFormatString;
				cOutputString++;
				cFormatString++;
				szLeft--;
		}
	}
done:
	if(szLeft > 0){
		*cOutputString = '\0';
		return (long)maxStringSize - (long)szLeft;
	}
	return 0;
}

//*******************************************************************************
//* strnumber関数																*
//*******************************************************************************
//* 数値文字列変換関数(getLocalTimeStringでのみ使用)
//* parameter	I	num					:入力数値
//*				I	digits				:出力桁数
//*				O	out					:出力バッファ
//*				I/O	count				:出力バッファ容量(残バッファ容量)
static void strnumber(
	int				num,
	const int		digits,
	char		**	out,
	size_t		*	count)
{
	int i;
	if(digits < (int)*count){
		for (i = 1;(int)(digits - i) >=0 ; i++) {
			*(*out + digits - i) = '0' + num % 10;
			num = (num - num % 10) / 10;
		}
		count = count - digits;
		*out = *out + digits;
	}
	else{
		*count = 0;
	}
}

#ifndef WIN32
//*******************************************************************************
//* ExpandEnvironmentStrings関数												*
//*******************************************************************************
//* 文字列中の環境変数を展開する(win32APIをUNIXでエミュレート)
//* parameter	I	szInputBuffer		:入力文字列バッファ
//*				O	szOutputBuffer		:出力文字列バッファ
//*				I	nSize				:出力バッファ長
//* return		0以上					:文字列変換成功(文字列長)
//*				0						:文字列変換失敗
int ExpandEnvironmentStrings(
	const char	*	szInputBuffer,
	char		*	szOutputBuffer,
	int				nSize)
{
	char * pszInputBuffer;
	char * pszTmpL;
	char * pszTmpR;
	char * pszEnv;

	//使用変数の初期化
	pszInputBuffer = (char *)malloc(strlen(szInputBuffer)+1);
	strcpy(pszInputBuffer,szInputBuffer);
	*szOutputBuffer = '\0';
	pszTmpL = pszInputBuffer;
	pszTmpR = strchr(pszTmpL,'$');

	//環境変数が発見されなくなるまでループ
	while(pszTmpR != NULL){
		//環境変数開始位置で文字列を切り離す
		*pszTmpR = '\0';
		//環境変数より左側を出力バッファに追加する
		strncat(szOutputBuffer,pszTmpL,nSize);
		nSize -= strlen(pszTmpL);
		pszTmpR++;
		//$の後ろが括弧の場合は括弧の中の文字列を環境変数名とする
		if(*pszTmpR=='('){
			//環境変数名を抜き出す
			pszTmpR++;
			pszTmpL = strchr(pszTmpR,')')+1;
			*(pszTmpL - 1)	= '\0';
			//環境変数から文字列を取得
			pszEnv = getenv(pszTmpR);
			if(pszEnv != NULL){
				strncat(szOutputBuffer,pszEnv,nSize);
				nSize -= strlen(pszEnv);
			}
		}
		else{
			//$の後ろが括弧でない場合は/の直前までを環境変数名とする
			pszTmpL = strchr(pszTmpR,'/');
			if(pszTmpL != NULL){
				*pszTmpL = '\0';
				*pszTmpL++;
				pszEnv = getenv(pszTmpR);
				if(pszEnv != NULL){
					strncat(szOutputBuffer,pszEnv,nSize);
					nSize -= strlen(pszEnv);
				}
				strncat(szOutputBuffer,"/",nSize);
				nSize -= strlen("/");
			}
			else{
				//'/'が文字列中にない場合は文字列全体を環境変数名とする
				pszEnv = getenv(pszTmpR);
				if(pszEnv != NULL){
					strncat(szOutputBuffer,pszEnv,nSize);
					nSize -= strlen(pszEnv);
				}
				//終了処理
				free(pszInputBuffer);
				return strlen(szOutputBuffer);
			}
		}
		//次の環境変数を検索する
		pszTmpR = strchr(pszTmpL,'$');
	}
	//残文字列を出力バッファに追加する
	strncat(szOutputBuffer,pszTmpL,nSize);
	free(pszInputBuffer);
	return strlen(szOutputBuffer);
}
#endif

//*******************************************************************************
//* getEnvString関数															*
//*******************************************************************************
//* 環境変数を取得する。取得できない場合は初期値を出力する
//* parameter	O	outputBuffer		:出力バッファ
//*				I	inputEnvName		:入力環境変数名
//*				I	DefaultString		:初期値
//*				I	maxOutputSize		:出力バッファのサイズ(NULL込み)
//* return		0以上					:文字列変換成功(文字列長)
//*				0						:文字列変換失敗
int getEnvString(
	char		*	outputBuffer,
	const char	*	inputEnvName,
	const char	*	DefaultString,
	int maxOutputSize)
{
	char * pszEnv;						//環境変数

	pszEnv = getenv(inputEnvName);	//環境変数より文字列を取得
	if(pszEnv != NULL){
		return ExpandEnvironmentStrings(pszEnv,outputBuffer,maxOutputSize);
	}
	else{	//取得できなかった場合は初期値を出力
		return ExpandEnvironmentStrings(DefaultString,outputBuffer,maxOutputSize);
	}
}

//*******************************************************************************
//* getEnvLong関数																*
//*******************************************************************************
//* 環境変数を取得する。取得できない場合は初期値を出力する
//* parameter	I	inputEnvName		:入力環境変数名
//*				I	DefaultString		:初期値
//* return								:取得した数値
long getEnvLong(
	const char	*	inputEnvName,
	const long		DefaultData)
{
	char * pszEnv;						//環境変数

	pszEnv = getenv(inputEnvName);	//環境変数より文字列を取得
	if(pszEnv != NULL){
		return strtol(pszEnv,NULL,0);
	}
	else{	//取得できなかった場合は初期値を出力
		return DefaultData;
	}
}

//*******************************************************************************
//* strtokEX関数																*
//*******************************************************************************
//* 文字列を指定された文字で区切り、トークンを切り出す
//* parameter	I/O	pcStartString		:入力文字列バッファ(切り出し後の残りバッファ)
//*				I	pcTokenSep			:初期値
//*				I	iOption				:オプション(オプション同士をANDで組み合わせ可能)
//* return								:切り出した文字列
//* 備考:オプションは以下の通り
//*		1		:"〜"の部分を1つの文字列として認識しその中で区切らないようにする
//*		2		:'〜'の部分を1つの文字列として認識しその中で区切らないようにする
//*		4		:(〜)の部分を1つの文字列として認識しその中で区切らないようにする
char * strtokEX(
	char		**	pcStartString,
	const char	*	pcTokenSep,
	int				iOption)
{
	char * pcToken;
	int i,j;
	char cSectionStack[50];		//待避スタックバッファ
	
	memset(cSectionStack,'\0',sizeof(cSectionStack));
	
	//区切り文字を検索する
	pcToken=*pcStartString+strspn(*pcStartString,pcTokenSep);
	
	for(i=0,j=0;((strchr(pcTokenSep,*(pcToken+i))==NULL)&&(j==0))||j>0;i++){
		if(*(pcToken+i)=='\0'){
			break;
		}
		switch (*(pcToken+i)){
			case '"':
			case '\'':
				if(((iOption&0x01) != 0)&&(*(pcToken+i)=='"')){
					break;
				}
				if(((iOption&0x02) != 0)&&(*(pcToken+i)=='\'')){
					break;
				}
				if(j>0){
					if(*(cSectionStack+j-1)==*(pcToken+i)){
						*(cSectionStack+j-1)='\0';
						j--;
					}
					else{
						*(cSectionStack+j)=*(pcToken+i);
						j++;
					}
				}
				else{
					*(cSectionStack+j)=*(pcToken+i);
					j++;
				}
				break;
			case '(':
				if((iOption & 0x04) != 0){
					break;
				}
				*(cSectionStack+j)='(';
				j++;
				break;
			case ')':
				if((iOption & 0x04) != 0){
					break;
				}
				if(j==0){
					j--;
					break;
				}
				if(*(cSectionStack+j-1)=='('){
					*(cSectionStack+j-1)='\0';
					j--;
				}
				break;
		}
		if(j<0){
			break;
		}
	}
	if(*(pcToken+i)!='\0'){
		*(pcToken+i)='\0';
		*pcStartString = pcToken+i+1;
	}
	else{
		*pcStartString = NULL;
	}
	return pcToken;
}

//*******************************************************************************
//* strTrim関数																	*
//*******************************************************************************
//* 文字列の前後の空白を除去する
//* parameter	I/O	inputBuffer			:入出力バッファ
//* return								:空白除去後の文字列の先頭アドレス
char * strTrim(char * inputBuffer){
	long i;
	while(*inputBuffer == ' '){
		inputBuffer++;
	}
	for(i=(long)strlen(inputBuffer)-1;*(inputBuffer+i)==' ';i--){
		*(inputBuffer+i)='\0';
	}
	return inputBuffer;
}

char * strLTrim(char * inputBuffer){
	while(*inputBuffer == ' '){
		inputBuffer++;
	}
	return inputBuffer;
}

//*******************************************************************************
//* 排他ロック開始関数																*
//*******************************************************************************
//* 排他ロックを行う
//* parameter	I	mutexNo			:ロック番号
int startLock(int mutexNo){
	if ( mutexNo < 0 || mutexNo >= mutexCount ) return ( -1 ) ;
	#ifndef WIN32
		pthread_mutex_lock( &mutex[mutexNo] );
	#else
		WaitForSingleObject(hMutex[mutexNo],INFINITE);
	#endif
	return ( 0 ) ;
}

//*******************************************************************************
//* 排他ロック解除関数															*
//*******************************************************************************
//* 排他ロックを解除する
//* parameter	I	mutexNo			:ロック番号
int endLock(int mutexNo){
	if ( mutexNo < 0 || mutexNo >= mutexCount ) return ( -1 ) ;
	#ifndef WIN32
		pthread_mutex_unlock( &mutex[mutexNo] ) ;
	#else
		ReleaseMutex( hMutex[mutexNo] );
	#endif
	return ( 0 ) ;
}

//*******************************************************************************
//* 排他ロック初期化関数															*
//*******************************************************************************
//* 排他ロックを初期化する
//* parameter	I	mutexNum		:ロック数(最大10)
int initLock(int mutexNum){
	if ( mutexCount != 0 ) return ( -2 ) ;
	if ( mutexNum < 1 || mutexNum > maxMutexLock ) return ( -1 ) ;
	for ( int i = 0 ; i < mutexNum ;i++ ) {
		#ifndef WIN32
			pthread_mutex_init ( &mutex[i],NULL ) ;
		#else
			hMutex[i] = CreateMutex(NULL,FALSE,NULL);
		#endif
	}
	mutexCount = mutexNum ;
	return ( 0 ) ;
}