mirror of
				https://github.com/chibicitiberiu/rainmeter-studio.git
				synced 2024-02-24 04:33:31 +00:00 
			
		
		
		
	- Fixed: Rainmeter crashes if the Lua GetStringValue() function returns an invalid value
- Fixed: Rainmeter crashes on initialization if Lua is invalid (e.g. syntax error) - Fixed: Rainmeter crashes on mouse action if Lua file is not initialized - Fixed: Measure=SCRIPT tries to process mouse actions even if corresponding function does not exist in the Lua file causing log to fill with 'nil value' warnings - Mainly cosmetic changes to logging of erros in Measure=SCRIPT
This commit is contained in:
		| @@ -22,7 +22,6 @@ CMeasureScript::CMeasureScript(CMeterWindow* meterWindow) : CMeasure(meterWindow | ||||
| CMeasureScript::~CMeasureScript() | ||||
| { | ||||
| 	delete m_pLuaScript; | ||||
|  | ||||
| } | ||||
|  | ||||
| void CMeasureScript::Initialize() | ||||
| @@ -92,26 +91,33 @@ const WCHAR* CMeasureScript::GetStringValue(AUTOSCALE autoScale, double scale, i | ||||
| 	return CMeasure::GetStringValue(autoScale, scale, decimals, percentual); | ||||
| } | ||||
|  | ||||
| static void stackDump (lua_State *L) { | ||||
|       int i=lua_gettop(L); | ||||
|       LuaManager::LuaLog(" ----------------  Stack Dump ----------------" ); | ||||
|       while(  i   ) { | ||||
|         int t = lua_type(L, i); | ||||
|         switch (t) { | ||||
|           case LUA_TSTRING: | ||||
| 			  LuaManager::LuaLog("%d:`%s'", i, lua_tostring(L, i)); | ||||
|           break; | ||||
|           case LUA_TBOOLEAN: | ||||
|             LuaManager::LuaLog("%d: %s",i,lua_toboolean(L, i) ? "true" : "false"); | ||||
|           break; | ||||
|           case LUA_TNUMBER: | ||||
|             LuaManager::LuaLog("%d: %g",  i, lua_tonumber(L, i)); | ||||
|          break; | ||||
|          default: LuaManager::LuaLog("%d: %s", i, lua_typename(L, t)); break; | ||||
|         } | ||||
|        i--; | ||||
|       } | ||||
|      LuaManager::LuaLog("--------------- Stack Dump Finished ---------------" ); | ||||
| static void stackDump(lua_State *L) | ||||
| { | ||||
| 	int i = lua_gettop(L); | ||||
| 	LuaManager::LuaLog(LOG_DEBUG, " ----------------  Stack Dump ----------------" ); | ||||
| 	while(i) | ||||
| 	{ | ||||
| 		int t = lua_type(L, i); | ||||
| 		switch (t) | ||||
| 		{ | ||||
| 		case LUA_TSTRING: | ||||
| 			LuaManager::LuaLog(LOG_DEBUG, "%d:`%s'", i, lua_tostring(L, i)); | ||||
| 			break; | ||||
|  | ||||
| 		case LUA_TBOOLEAN: | ||||
| 			LuaManager::LuaLog(LOG_DEBUG, "%d: %s",i,lua_toboolean(L, i) ? "true" : "false"); | ||||
| 			break; | ||||
|  | ||||
| 		case LUA_TNUMBER: | ||||
| 			LuaManager::LuaLog(LOG_DEBUG, "%d: %g",  i, lua_tonumber(L, i)); | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			LuaManager::LuaLog(LOG_DEBUG, "%d: %s", i, lua_typename(L, t)); break; | ||||
| 		} | ||||
| 		i--; | ||||
| 	} | ||||
| 	LuaManager::LuaLog(LOG_DEBUG, "--------------- Stack Dump Finished ---------------" ); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -123,98 +129,97 @@ static void stackDump (lua_State *L) { | ||||
| */ | ||||
| void CMeasureScript::ReadConfig(CConfigParser& parser, const WCHAR* section) | ||||
| { | ||||
|  | ||||
| 	CMeasure::ReadConfig(parser, section); | ||||
|  | ||||
| 	std::string strScriptFile = ConvertToAscii(parser.ReadString(section, L"ScriptFile", L"").c_str()); | ||||
| 	std::string strTableName = ConvertToAscii(parser.ReadString(section, L"TableName", L"").c_str()); | ||||
|  | ||||
| 	if(strScriptFile.empty() == false) | ||||
| 	if (strScriptFile.empty() == false) | ||||
| 	{ | ||||
|  | ||||
| 		if(strTableName.empty() == false) | ||||
| 		if (strTableName.empty() == false) | ||||
| 		{ | ||||
| 			lua_State* L = LuaManager::GetState(); | ||||
|  | ||||
| 			m_pLuaScript = new LuaScript(LuaManager::GetState(), strScriptFile.c_str(), strTableName.c_str()); | ||||
| 			 | ||||
| 			m_bUpdateDefined = m_pLuaScript->FunctionExists(g_strUpdateFunction); | ||||
| 			m_bInitializeDefined = m_pLuaScript->FunctionExists(g_strInitFunction); | ||||
| 			m_bGetValueDefined = m_pLuaScript->FunctionExists(g_strGetValueFunction); | ||||
| 			m_bGetStringValueDefined = m_pLuaScript->FunctionExists(g_strGetStringValueFunction); | ||||
|  | ||||
|  | ||||
| 			m_pLuaScript->PushTable(); | ||||
|  | ||||
| 			// Push the variable name we want to put a value in. | ||||
| 			lua_pushstring(L, "SELF"); | ||||
| 			// Push the value | ||||
| 			tolua_pushusertype(L, this, "CMeasure"); | ||||
| 			// Bind the variable | ||||
| 			lua_settable(L, -3); | ||||
|  | ||||
| 			// Push the variable name we want to put a value in. | ||||
| 			lua_pushstring(L, "SKIN"); | ||||
| 			// Push the value | ||||
| 			tolua_pushusertype(L, m_MeterWindow, "CMeterWindow"); | ||||
| 			// Bind the variable | ||||
| 			lua_settable(L, -3); | ||||
|  | ||||
| 			// Push the variable name we want to put a value in. | ||||
| 			lua_pushstring(L, "RAINMETER"); | ||||
| 			// Push the value | ||||
| 			tolua_pushusertype(L, m_MeterWindow->GetMainObject(), "CRainmeter"); | ||||
| 			// Bind the variable | ||||
| 			lua_settable(L, -3); | ||||
|  | ||||
| 			// Look i nthe properties table for values to read from the section. | ||||
| 			lua_getfield(L, -1, "PROPERTIES"); | ||||
| 			if(lua_isnil(L, -1) == 0) | ||||
| 			if (m_pLuaScript->IsInitialized()) | ||||
| 			{ | ||||
| 				lua_pushnil(L); | ||||
| 				m_bUpdateDefined = m_pLuaScript->FunctionExists(g_strUpdateFunction); | ||||
| 				m_bInitializeDefined = m_pLuaScript->FunctionExists(g_strInitFunction); | ||||
| 				m_bGetValueDefined = m_pLuaScript->FunctionExists(g_strGetValueFunction); | ||||
| 				m_bGetStringValueDefined = m_pLuaScript->FunctionExists(g_strGetStringValueFunction); | ||||
|  | ||||
| 				while(lua_next(L, -2))  | ||||
| 				{   | ||||
| 					lua_pop(L, 1); | ||||
| 				m_pLuaScript->PushTable(); | ||||
|  | ||||
| 					const char* strKey = lua_tostring(L, -1); | ||||
| 				// Push the variable name we want to put a value in. | ||||
| 				lua_pushstring(L, "SELF"); | ||||
| 				// Push the value | ||||
| 				tolua_pushusertype(L, this, "CMeasure"); | ||||
| 				// Bind the variable | ||||
| 				lua_settable(L, -3); | ||||
|  | ||||
| 					std::wstring wstrKey = ConvertToWide(strKey); | ||||
| 					std::wstring wstrValue = parser.ReadString(section, wstrKey.c_str(), L"").c_str(); | ||||
| 					 | ||||
| 					if( !wstrValue.empty() ) | ||||
| 				// Push the variable name we want to put a value in. | ||||
| 				lua_pushstring(L, "SKIN"); | ||||
| 				// Push the value | ||||
| 				tolua_pushusertype(L, m_MeterWindow, "CMeterWindow"); | ||||
| 				// Bind the variable | ||||
| 				lua_settable(L, -3); | ||||
|  | ||||
| 				// Push the variable name we want to put a value in. | ||||
| 				lua_pushstring(L, "RAINMETER"); | ||||
| 				// Push the value | ||||
| 				tolua_pushusertype(L, m_MeterWindow->GetMainObject(), "CRainmeter"); | ||||
| 				// Bind the variable | ||||
| 				lua_settable(L, -3); | ||||
|  | ||||
| 				// Look i nthe properties table for values to read from the section. | ||||
| 				lua_getfield(L, -1, "PROPERTIES"); | ||||
| 				if (lua_isnil(L, -1) == 0) | ||||
| 				{ | ||||
| 					lua_pushnil(L); | ||||
|  | ||||
| 					while(lua_next(L, -2))  | ||||
| 					{ | ||||
| 						const wchar_t* wstrValue2 = wstrValue.c_str(); | ||||
| 						std::string strStrVal = ConvertToAscii(wstrValue2); | ||||
| 						const char* strValue = strStrVal.c_str(); | ||||
| 						lua_pop(L, 1); | ||||
|  | ||||
| 						const char* strKey = lua_tostring(L, -1); | ||||
|  | ||||
| 						std::wstring wstrKey = ConvertToWide(strKey); | ||||
| 						std::wstring wstrValue = parser.ReadString(section, wstrKey.c_str(), L"").c_str(); | ||||
| 						 | ||||
| 						lua_pushstring(L,strValue); | ||||
| 						lua_settable(L, -3); | ||||
| 						if (!wstrValue.empty()) | ||||
| 						{ | ||||
| 							const wchar_t* wstrValue2 = wstrValue.c_str(); | ||||
| 							std::string strStrVal = ConvertToAscii(wstrValue2); | ||||
| 							const char* strValue = strStrVal.c_str(); | ||||
| 							 | ||||
| 							lua_pushstring(L,strValue); | ||||
| 							lua_settable(L, -3); | ||||
|  | ||||
| 							lua_pushstring(L,strKey); | ||||
| 						} | ||||
|  | ||||
| 						lua_pushstring(L,strKey); | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
| 			} | ||||
| 			// Pop PROPERTIES table | ||||
| 			lua_pop(L, 1); | ||||
| 				// Pop PROPERTIES table | ||||
| 				lua_pop(L, 1); | ||||
|  | ||||
| 			// Pop the table | ||||
| 			lua_pop(L, 1);	 | ||||
| 				// Pop the table | ||||
| 				lua_pop(L, 1); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			LuaManager::LuaLog("%s : TableName missing.", m_ANSIName.c_str()); | ||||
| 			LuaManager::LuaLog(LOG_ERROR, "Script: TableName missing in %s.", m_ANSIName.c_str()); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		LuaManager::LuaLog("%s : ScriptFile missing.", m_ANSIName.c_str()); | ||||
| 		LuaManager::LuaLog(LOG_ERROR, "Script: ScriptFile missing in %s.", m_ANSIName.c_str()); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| void CMeasureScript::RunFunctionWithMeter(const char* p_strFunction, CMeter* p_pMeter) | ||||
| { | ||||
|  | ||||
| 	// Get the Lua State | ||||
| 	lua_State* L = m_pLuaScript->GetState(); | ||||
|  | ||||
| @@ -222,14 +227,18 @@ void CMeasureScript::RunFunctionWithMeter(const char* p_strFunction, CMeter* p_p | ||||
| 	m_pLuaScript->PushTable(); | ||||
|  | ||||
| 	// Push the function onto the stack | ||||
| 	lua_getfield(L,-1, p_strFunction); | ||||
| 	lua_getfield(L, -1, p_strFunction); | ||||
|  | ||||
| 	// Push the Meter | ||||
| 	tolua_pushusertype(L, (void*) p_pMeter ,"CMeter"); | ||||
|  | ||||
| 	if( lua_pcall(L, 1, 0, 0) ) | ||||
| 	// Check if the function exists | ||||
| 	if (!lua_isnil(L, -1)) | ||||
| 	{ | ||||
| 		LuaManager::ReportErrors(L); | ||||
| 		// Push the Meter | ||||
| 		tolua_pushusertype(L, (void*) p_pMeter, "CMeter"); | ||||
|  | ||||
| 		if (lua_pcall(L, 1, 0, 0)) | ||||
| 		{ | ||||
| 			LuaManager::ReportErrors(L); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Clean up | ||||
| @@ -238,36 +247,45 @@ void CMeasureScript::RunFunctionWithMeter(const char* p_strFunction, CMeter* p_p | ||||
|  | ||||
| void CMeasureScript::MeterMouseEvent(CMeter* p_pMeter, MOUSE p_eMouse) | ||||
| { | ||||
| 	switch (p_eMouse) | ||||
| 	if (m_pLuaScript->IsInitialized()) | ||||
| 	{ | ||||
| 		switch (p_eMouse) | ||||
| 		{ | ||||
| 		case MOUSE_LMB_DOWN: | ||||
| 			RunFunctionWithMeter("LeftMouseDown", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_LMB_UP: | ||||
| 			RunFunctionWithMeter("LeftMouseUp", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_LMB_DBLCLK: | ||||
| 			RunFunctionWithMeter("LeftMouseDoubleClick", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_RMB_DOWN: | ||||
| 			RunFunctionWithMeter("RightMouseDown", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_RMB_UP: | ||||
| 			RunFunctionWithMeter("RightMouseUp", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_RMB_DBLCLK: | ||||
| 			RunFunctionWithMeter("RightMouseDoubleClick", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_MMB_DOWN: | ||||
| 			RunFunctionWithMeter("MiddleMouseDown", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_MMB_UP: | ||||
| 			RunFunctionWithMeter("MiddleMouseUp", p_pMeter); | ||||
| 			break; | ||||
|  | ||||
| 		case MOUSE_MMB_DBLCLK: | ||||
| 			RunFunctionWithMeter("MiddleMouseDoubleClick", p_pMeter); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -12,7 +12,6 @@ | ||||
|  | ||||
| #include "lua_rainmeter_ext.h" | ||||
|  | ||||
|  | ||||
| bool LuaManager::m_bInitialized = false; | ||||
| lua_State* LuaManager::m_pState = 0; | ||||
|  | ||||
| @@ -20,7 +19,6 @@ void LuaManager::Init() | ||||
| { | ||||
| 	if(m_pState == 0) | ||||
| 	{ | ||||
|  | ||||
| 		// initialize Lua  | ||||
| 		m_pState = lua_open(); | ||||
|  | ||||
| @@ -36,7 +34,6 @@ void LuaManager::Init() | ||||
| 		//tolua_meter_image_open(m_pState); | ||||
|  | ||||
| 		luaopen_rainmeter_ext(m_pState); | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -47,13 +44,11 @@ void LuaManager::CleanUp() | ||||
|  | ||||
| void LuaManager::ReportErrors(lua_State * L) | ||||
| { | ||||
|  | ||||
| 	LuaLog("Lua Error: %s", lua_tostring(L, -1)); | ||||
| 	LuaLog(LOG_ERROR, "Script: %s", lua_tostring(L, -1)); | ||||
| 	lua_pop(L, 1); | ||||
|  | ||||
| }  | ||||
|  | ||||
| void LuaManager::LuaLog(const char* format, ... ) | ||||
| void LuaManager::LuaLog(int nLevel, const char* format, ... ) | ||||
| { | ||||
| 	char buffer[4096]; | ||||
| 	va_list args; | ||||
| @@ -66,7 +61,7 @@ void LuaManager::LuaLog(const char* format, ... ) | ||||
| 	_vsnprintf_s( buffer, _TRUNCATE, format, args ); | ||||
| 	if (errno != 0) | ||||
| 	{ | ||||
| 		_snprintf_s(buffer, _TRUNCATE, "LuaLog internal error: %s", format); | ||||
| 		_snprintf_s(buffer, _TRUNCATE, "Script: LuaLog internal error: %s", format); | ||||
| 	} | ||||
|  | ||||
| 	_set_invalid_parameter_handler(oldHandler); | ||||
| @@ -79,6 +74,6 @@ void LuaManager::LuaLog(const char* format, ... ) | ||||
|  | ||||
| 	std::wstring str = ConvertToWide(buffer); | ||||
|  | ||||
| 	LSLog(LOG_NOTICE, L"Lua", str.c_str()); | ||||
| 	LSLog(nLevel, L"Rainmeter", str.c_str()); | ||||
| 	va_end(args); | ||||
| } | ||||
| @@ -16,7 +16,7 @@ public: | ||||
| 	 | ||||
| 	static void ReportErrors(lua_State * L); | ||||
|  | ||||
| 	static void LuaLog(const char* format, ... ); | ||||
| 	static void LuaLog(int nLevel, const char* format, ... ); | ||||
|  | ||||
| protected: | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ LuaScript::LuaScript(lua_State* p_pState, const char* p_strFile, const char* p_s | ||||
| 	int result = luaL_loadfile(m_pState, p_strFile); | ||||
| 	 | ||||
| 	// If the file loaded okay. | ||||
| 	if(result == 0) | ||||
| 	if (result == 0) | ||||
| 	{ | ||||
| 		// Create the table this script will reside in | ||||
| 		lua_newtable(m_pState); | ||||
| @@ -38,20 +38,19 @@ LuaScript::LuaScript(lua_State* p_pState, const char* p_strFile, const char* p_s | ||||
| 		// Execute the Lua script | ||||
| 		result = lua_pcall(m_pState, 0, LUA_MULTRET, 0); | ||||
|  | ||||
| 		if(result) | ||||
| 		if (result) | ||||
| 		{ | ||||
| 			m_bInitialized = false; | ||||
| 			LuaManager::LuaLog("Lua cannot run file:"); | ||||
| 			LuaManager::LuaLog("%s", lua_tostring(m_pState, -1) ); | ||||
| 			LuaManager::LuaLog(LOG_ERROR, "Script: Cannot run file: %s", p_strFile); | ||||
| 			LuaManager::LuaLog(LOG_ERROR, "Script: %s", lua_tostring(m_pState, -1)); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		m_bInitialized = false; | ||||
| 		LuaManager::LuaLog("Lua cannot run file:"); | ||||
| 		LuaManager::LuaLog("%s", lua_tostring(m_pState, -1) ); | ||||
| 		LuaManager::LuaLog(LOG_ERROR, "Script: Cannot run file: %s", p_strFile); | ||||
| 		LuaManager::LuaLog(LOG_ERROR, "Script: %s", lua_tostring(m_pState, -1)); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| LuaScript::~LuaScript(void) | ||||
| @@ -61,7 +60,6 @@ LuaScript::~LuaScript(void) | ||||
|  | ||||
| void LuaScript::BindVariable(const char* p_strName, void* p_pValue, const char* p_strTypeName) | ||||
| { | ||||
|  | ||||
| 	PushTable(); | ||||
|  | ||||
| 	/* | ||||
| @@ -89,7 +87,7 @@ void LuaScript::BindVariable(const char* p_strName, void* p_pValue, const char* | ||||
|  | ||||
| double LuaScript::RunFunctionDouble(const char* p_strFuncName) | ||||
| { | ||||
| 	if( m_bInitialized && p_strFuncName ) | ||||
| 	if (m_bInitialized && p_strFuncName) | ||||
| 	{ | ||||
| 		// Push our table onto the stack | ||||
| 		lua_getglobal(m_pState, m_strTableName); | ||||
| @@ -97,7 +95,7 @@ double LuaScript::RunFunctionDouble(const char* p_strFuncName) | ||||
| 		// Push the function onto the stack | ||||
| 		lua_getfield(m_pState,-1, p_strFuncName); | ||||
|  | ||||
| 		if( lua_pcall(m_pState, 0, 1, 0) ) | ||||
| 		if(lua_pcall(m_pState, 0, 1, 0)) | ||||
| 		{ | ||||
| 			LuaManager::ReportErrors(m_pState); | ||||
| 		} | ||||
| @@ -105,7 +103,7 @@ double LuaScript::RunFunctionDouble(const char* p_strFuncName) | ||||
| 		{ | ||||
| 			if (!lua_isnumber(m_pState, -1)) | ||||
| 			{ | ||||
| 				LuaManager::LuaLog("Function `%s:%s' must return a number",m_strTableName, p_strFuncName); | ||||
| 				LuaManager::LuaLog(LOG_ERROR, "Script: Function '%s:%s' must return a number", m_strTableName, p_strFuncName); | ||||
| 			} | ||||
|  | ||||
| 			double d = lua_tonumber(m_pState, -1); | ||||
| @@ -121,7 +119,6 @@ double LuaScript::RunFunctionDouble(const char* p_strFuncName) | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
|  | ||||
| std::wstring LuaScript::RunFunctionString(const char* p_strFuncName) | ||||
| { | ||||
| 	if( m_bInitialized && p_strFuncName ) | ||||
| @@ -130,9 +127,9 @@ std::wstring LuaScript::RunFunctionString(const char* p_strFuncName) | ||||
| 		lua_getglobal(m_pState, m_strTableName); | ||||
|  | ||||
| 		// Push the function onto the stack | ||||
| 		lua_getfield(m_pState,-1, p_strFuncName); | ||||
| 		lua_getfield(m_pState, -1, p_strFuncName); | ||||
|  | ||||
| 		if( lua_pcall(m_pState, 0, 1, 0) ) | ||||
| 		if (lua_pcall(m_pState, 0, 1, 0)) | ||||
| 		{ | ||||
| 			LuaManager::ReportErrors(m_pState); | ||||
| 		} | ||||
| @@ -140,7 +137,7 @@ std::wstring LuaScript::RunFunctionString(const char* p_strFuncName) | ||||
| 		{ | ||||
| 			if (!lua_isstring(m_pState, -1)) | ||||
| 			{ | ||||
| 				LuaManager::LuaLog("Function `%s:%s' must return a string",m_strTableName, p_strFuncName); | ||||
| 				LuaManager::LuaLog(LOG_ERROR, "Script: Function '%s:%s' must return a string",m_strTableName, p_strFuncName); | ||||
| 			} | ||||
|  | ||||
| 			const char* str = lua_tostring(m_pState, -1); | ||||
| @@ -153,7 +150,7 @@ std::wstring LuaScript::RunFunctionString(const char* p_strFuncName) | ||||
| 		lua_pop(m_pState, 1); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| 	return L""; | ||||
| } | ||||
|  | ||||
| void LuaScript::RunFunction(const char* p_strFuncName) | ||||
|   | ||||
| @@ -91,7 +91,7 @@ static int AsMeterString(lua_State* tolua_S) | ||||
| static int staticLuaLog(lua_State* tolua_S) | ||||
| { | ||||
| 	const char* str = tolua_tostring(tolua_S,1,0); | ||||
| 	LuaManager::LuaLog(str); | ||||
| 	LuaManager::LuaLog(LOG_NOTICE, str); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Birunthan Mohanathas
					Birunthan Mohanathas