/* =============================================================================
 *  scpp_env.cpp
 *  Copyright (c)  2003 - 2011  Nomura Kei
 *  LICENSE :
 *  LGPL (GNU Lesser General General Public License - Version 3,29 June 2007)
 *  http://www.gnu.org/copyleft/lesser.html
 * =============================================================================
 *
 * 環境変数操作
 *
 * 環境変数の値を取得/設定/削除します
 *
 */
#include <iostream>
#include <cstdlib>

#include <scpp_os.hpp>
#include <scpp_errno.hpp>
#include <scpp_env.hpp>


namespace scpp
{

namespace Env
{

/// 環境変数用一時バッファサイズ (現状, Windows でのみ使用される)
const int SCPP_ENV_BUFFER_MAX = 4096;


/**
 * 指定された name の環境変数を取得します.
 * name に = が含まれる場合, = の前までをキーとして環境変数を検索します.
 *
 * 使用例)
 * @code
 * // sample.cpp
 * #include <iostream>
 * #include <scpp_env.hpp>
 *
 * int main(int argc, char* argv[])
 * {
 *     std::string envVal;
 *     bool ret = scpp::env::getEnv(&envVal, "LANG");
 *     if (ret)
 *     {
 *        std::cout << "LANG=" << envVal << std::endl;
 *     }
 *     return 0;
 * }
 * -----
 * $ g++ `scpp-config --cflags` -o sample sample.cpp `scpp-config --libs`
 * $ LANG=C
 * $ ./sample
 * LANG=C
 * @endcode
 *
 * @param buf  環境変数の値格納用文字バッファ
 * @param name 環境変数
 */
bool getEnv(std::string* buf, const std::string& name)
{
	// = より前までを nameKey に設定
	std::string::size_type index   = name.find("=");
	std::string            nameKey = (index == std::string::npos) ? name : name.substr(0, index);

#if (SCPP_IS_WINDOWS)
	// for Windows
	char  tmpBuf[SCPP_ENV_BUFFER_MAX];
	DWORD ret = GetEnvironmentVariable(nameKey.c_str(), (LPSTR) tmpBuf, sizeof(tmpBuf));
	if (ret != 0)
	{
		*buf    = tmpBuf;
		return true;
	}
	*buf = "";
	return false;

#else
	// for not Windows
	char* tmpBuf = getenv(nameKey.c_str());
	if (tmpBuf != 0)
	{
		*buf    = tmpBuf;
		return true;
	}
	*buf = "";
	return false;

#endif
}



/**
 * 環境変数設定.
 *
 * name が存在しない場合, 環境変数 name に値 value を設定し,
 * 環境に追加します. name が環境変数に存在する場合,
 * overwrite が true ならば, その値を value に変更します.
 * false ならば name の値を変更せず true を返します.
 *
 * name に '=' 文字が含まれている場合,
 * エラー番号に SCPP_EINVAL を設定し, エラーを返します.
 *
 * 使用例)
 * @code
 * // sample.cpp
 * #include <iostream>
 * #include <scpp_env.hpp>
 *
 * int main(int argc, char* argv[])
 * {
 *     bool ret = scpp::env::setEnv("LANG", "C");
 *     if (ret)
 *     {
 *         std::string envVal;
 *         scpp::env::getEnv(&envVal, "LANG", true);
 *         std::cout << "LANG=" << envVal << std::endl;
 *     }
 *     return 0;
 * }
 * -----
 * $ g++ `scpp-config --cflags` -o sample sample.cpp `scpp-config --libs`
 * $ ./sample
 * LANG=C
 * @endcode
 *
 * @param name      環境変数名
 * @param value     値
 * @param overwrite true/false (上書きする/しない)
 * @return true/false (設定成功/失敗)
 */
bool setEnv(const std::string& name, const std::string& value, bool overwrite)
{
#if (SCPP_IS_WINDOWS)
	// for Windows
	std::string::size_type index = name.find("=");
	if (index != std::string::npos)
	{	// = が含まれる
		Errno::setError(SCPP_EINVAL);
		return false;
		
	}

	// name が既に存在するか確認
	LPSTR buf[1];
	DWORD ret = GetEnvironmentVariable(name.c_str(), (LPSTR) buf, 1);
	if ((!overwrite) && (ret != 0))
	{	// 上書きしない場合, 既に環境変数にある -> true
		return true;
	}

	// 環境変数を設定する
	ret = SetEnvironmentVariable(name.c_str(), value.c_str());
	return (ret != 0);

#else
	// for Windows 以外
	// = が含まれている場合, setenv 内でエラーになる
	int ret = setenv(name.c_str(), value.c_str(), overwrite);
	return (ret == 0);

#endif

}



/**
 * 環境変数 name を環境から削除します.
 *
 * @param name 環境変数名
 * @return true/false (削除成功/失敗)
 */
bool unsetEnv(const std::string& name)
{
#if (SCPP_IS_WINDOWS)
	// for Windows
	BOOL ret = SetEnvironmentVariable(name.c_str(), 0);
	return (ret != 0);

#else
	// for Windows 以外
	int ret = unsetenv(name.c_str());
	return (ret == 0);

#endif

}


}	// namespace Env
}	// namespace scpp


