Пример #1
0
bool CDatum::CreateFromAttributeList (const CAttributeList &Attribs, CDatum *retdDatum)

//	CreateFromAttributeList
//
//	Creates a datum from an attribute list

	{
	int i;

	TArray<CString> AllAttribs;
	Attribs.GetAll(&AllAttribs);

	if (AllAttribs.GetCount() == 0)
		{
		*retdDatum = CDatum();
		return true;
		}

	CComplexArray *pArray = new CComplexArray;
	for (i = 0; i < AllAttribs.GetCount(); i++)
		pArray->Insert(AllAttribs[i]);

	*retdDatum = CDatum(pArray);
	return true;
	}
Пример #2
0
CDatum CHyperionScheduler::GetTaskList (void)

//	GetTaskList
//
//	Returns a list of tasks

	{
	CSmartLock Lock(m_cs);
	int i;

	CComplexArray *pResult = new CComplexArray;
	for (i = 0; i < m_Tasks.GetCount(); i++)
		{
		CComplexStruct *pTask = new CComplexStruct;
		pTask->SetElement(FIELD_NAME, m_Tasks[i].sName);
		pTask->SetElement(FIELD_STATUS, (m_Tasks[i].bRunning ? STATUS_RUNNING : STATUS_READY));
		pTask->SetElement(FIELD_LAST_RAN_ON, m_Tasks[i].LastRun);
		pTask->SetElement(FIELD_WILL_RUN_ON, m_Tasks[i].NextRun);
		pTask->SetElement(FIELD_RUN_FREQUENCY, m_Tasks[i].iInterval);

		pResult->Append(CDatum(pTask));
		}

	return CDatum(pResult);
	}
Пример #3
0
CDatum CMnemosynthDb::Read (const CString &sCollection, const CString &sKey) const

//	Read
//
//	Read an entry in a collection

	{
	CSmartLock Lock(m_cs);

	//	Look for the collection. If we can't find it, then we return Nil

	SCollection *pCol = m_Collections.GetAt(sCollection);
	if (pCol == NULL)
		return CDatum();

	//	Look for the key. If we can't find it, then we return Nil

	SEntry *pEntry = pCol->Entries.GetAt(sKey);
	if (pEntry == NULL)
		return CDatum();

	//	Return the value

	return pEntry->dValue;
	}
Пример #4
0
CDatum CComplexDateTime::GetElement (int iIndex) const

//	GetElement
//
//	Returns a dateTime component

	{
	switch (iIndex)
		{
		case partYear:
			return CDatum(m_DateTime.Year());

		case partMonth:
			return CDatum(m_DateTime.Month());

		case partDay:
			return CDatum(m_DateTime.Day());

		case partHour:
			return CDatum(m_DateTime.Hour());

		case partMinute:
			return CDatum(m_DateTime.Minute());

		case partSecond:
			return CDatum(m_DateTime.Second());

		case partMillisecond:
			return CDatum(m_DateTime.Millisecond());

		default:
			return CDatum();
		}
	}
Пример #5
0
CDatum CCryptosaurEngine::GenerateAuthToken (CDatum dData, DWORD dwLifetime)

//	GenerateAuthToken
//
//	Generates an authToken for the given user, scope, and lifetime.
//
//	If dwLifetime == 0 then the token does not expire.

	{
	//	Calculate the expiration time

	CDateTime ExpireTime;
	if (dwLifetime > 0)
		{
		DWORD dwLifetimeDays = dwLifetime / SECONDS_PER_DAY;
		DWORD dwLifetimeSeconds = dwLifetime % SECONDS_PER_DAY;
		ExpireTime = timeAddTime(CDateTime(CDateTime::Now), CTimeSpan(dwLifetimeDays, dwLifetimeSeconds * 1000));
		}
	else
		ExpireTime = CDateTime(CDateTime::BeginningOfTime);

	//	Get the key to sign with (the key is guaranteed to exist because we
	//	checked at boot time).

	CIPInteger *pAuthTokenKey = m_Keys.GetAt(KEY_CRYPTOSAUR_AUTH_TOKEN);

	//	Create the token

	CString sToken;
	CCryptosaurInterface::CreateAuthToken(dData, ExpireTime, *pAuthTokenKey, &sToken);

	return CDatum(sToken);
	}
Пример #6
0
bool CUserInfoSession::OnStartSession (const SArchonMessage &Msg, DWORD dwTicket)

//	OnStartSession
//
//	Start

	{
	//	Send an Aeon message to get the user record

	CComplexArray *pPayload = new CComplexArray;
	pPayload->Insert(USERS_TABLE_NAME);
	pPayload->Insert(m_sUsernameKey);

	//	Send message

	ISessionHandler::SendMessageCommand(ADDRESS_AEON_COMMAND,
			MSG_AEON_GET_VALUE,
			GenerateAddress(PORT_CRYPTOSAUR_COMMAND),
			CDatum(pPayload),
			MESSAGE_TIMEOUT);

	//	Expect reply

	return true;
	}
Пример #7
0
CDatum CDatum::GetElement (int iIndex) const

//	GetElement
//
//	Gets the appropriate element

	{
	ASSERT(iIndex >= 0);

	switch (m_dwData & AEON_TYPE_MASK)
		{
		case AEON_TYPE_COMPLEX:
			{
			if (iIndex == 0)
				{
				IComplexDatum *pComplex = raw_GetComplex();
				if (pComplex->IsArray())
					return raw_GetComplex()->GetElement(0);
				else
					return *this;
				}
			else
				return raw_GetComplex()->GetElement(iIndex);
			}

		default:
			return (iIndex == 0 ? *this : CDatum());
		}
	}
Пример #8
0
void CAeonEngine::MsgDeleteView (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgDeleteView
//
//	Aeon.deleteView {tableAndView}

	{
	CAeonTable *pTable;
	DWORD dwViewID;
	if (!ParseTableAndView(Msg, pSecurityCtx, Msg.dPayload.GetElement(0), &pTable, &dwViewID))
		return;

	//	Delete

	CString sError;
	if (!pTable->DeleteView(dwViewID, &sError))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg);
		return;
		}

	//	Done

	SendMessageReply(MSG_OK, CDatum(), Msg);
	}
Пример #9
0
void CAeonEngine::MsgGetTables (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgGetTables
//
//	Aeon.getTables

	{
	int i;

	if (!m_bReady)
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_NOT_READY, Msg);
		return;
		}

	//	Get a list of all tables

	TArray<CAeonTable *> AllTables;
	GetTables(&AllTables);

	//	Return an array of table descriptors

	CComplexArray *pArray = new CComplexArray;
	for (i = 0; i < AllTables.GetCount(); i++)
		{
		if (pSecurityCtx && !pSecurityCtx->IsNamespaceAccessible(AllTables[i]->GetName()))
			continue;

		pArray->Insert(AllTables[i]->GetDesc());
		}

	//	Reply

	SendMessageReply(MSG_AEON_RESULT_TABLES, CDatum(pArray), Msg);
	}
Пример #10
0
void CAeonView::CreateSecondaryData (const CTableDimensions &PrimaryDims, const CRowKey &PrimaryKey, CDatum dFullData, SEQUENCENUMBER RowID, CDatum *retdData)

//	CreateSecondaryData
//
//	Creates the data for a secondary view row.

	{
	int i, j;
	CComplexStruct *pData = new CComplexStruct;

	//	If the list of columns is empty then we just add the primary key

	if (m_Columns.GetCount() == 0)
		pData->SetElement(FIELD_PRIMARY_KEY, PrimaryKey.AsDatum(PrimaryDims));

	//	Otherwise we add all the fields listed in the columns array

	else
		{
		for (i = 0; i < m_Columns.GetCount(); i++)
			{
			//	The special string "primaryKey" means that we insert the 
			//	primary key as a special field.

			if (strEquals(m_Columns[i], FIELD_PRIMARY_KEY))
				pData->SetElement(FIELD_PRIMARY_KEY, PrimaryKey.AsDatum(PrimaryDims));

			//	The special string "*" means that we insert all existing
			//	fields.

			else if (strEquals(m_Columns[i], STR_ALL_COLUMNS))
				{
				for (j = 0; j < dFullData.GetCount(); j++)
					{
					CDatum dKey = dFullData.GetKey(j);
					CDatum dValue = dFullData.GetElement(j);

					if (!dValue.IsNil())
						pData->SetElement(dKey, dValue);
					}
				}

			//	Add the field by name.

			else
				{
				CDatum dColData = dFullData.GetElement(m_Columns[i]);
				if (!dColData.IsNil())
					pData->SetElement(m_Columns[i], dColData);
				}
			}
		}

	//	Done

	*retdData = CDatum(pData);
	}
Пример #11
0
void CMnemosynthDb::Delete (const CString &sCollection, const CString &sKey)

//	Delete
//
//	Delete an entry in a collection. This is just a write of Nil.

	{
	Write(sCollection, sKey, CDatum());
	}
Пример #12
0
void CEsperEngine::SendMessageReplyDisconnect (const SArchonMessage &OriginalMsg)

//	SendMessageReplyDisconnect
//
//	Reply with Esper.onDisconnect

	{
	m_pProcess->SendMessageReply(MSG_ESPER_ON_DISCONNECT, CDatum(), OriginalMsg);
	}
Пример #13
0
void CAeonEngine::MsgOnMachineStart (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgOnMachineStart
//
//	Exarch.onMachineStart

	{
	CSmartLock Lock(m_cs);

	//	If we've already started, then nothing to do.

	if (m_bMachineStarted)
		return;

	//	Check to see if we're listed in the module list. If we're not, then we 
	//	continue waiting.

	if (!Msg.dPayload.IsNil() && !Msg.dPayload.Find(GetProcessCtx()->GetModuleName()))
		return;

	//	Aeon starting

	Log(MSG_LOG_INFO, STR_AEON_STARTING);

	//	Make a list of local volumes

	if (!OpenLocalVolumes())
		return;

	//	We've at least gotten to onMachineStart. From now on if we get any 
	//	storage added, we have to deal with it differently.

	m_bMachineStarted = true;

	//	If we have no local volumes then we cannot proceed. We wait until we
	//	have storage to send Aeon.onStart.

	if (m_LocalVolumes.GetCount() == 0)
		{
		Log(MSG_LOG_INFO, STR_ERROR_NO_LOCAL_STORAGE);
		return;
		}

	//	Make sure the database is open

	if (!Open())
		return;

	//	Tell any listeners that the database is ready

	SendMessageNotify(ADDRESS_AEON_NOTIFY, MSG_AEON_ON_START, CDatum());

	//	Done

	Log(MSG_LOG_INFO, STR_DATABASE_OPEN);
	}
Пример #14
0
bool CDatum::CreateFromStringValue (const CString &sValue, CDatum *retdDatum)

//	CreateFromStringValue
//
//	Creates either a string or a number depending on the value.

	{
	CDatum dDatum;

	switch (GetStringValueType(sValue))
		{
		case typeNil:
			break;

		case typeInteger32:
			dDatum = CDatum(strToInt(sValue, 0));
			break;

		case typeIntegerIP:
			{
			CIPInteger Value;
			Value.InitFromString(sValue);
			CDatum::CreateIPIntegerFromHandoff(Value, &dDatum);
			break;
			}

		case typeDouble:
			dDatum = CDatum(strToDouble(sValue));
			break;

		case typeString:
			dDatum = CDatum(sValue);
			break;

		default:
			return false;
		}

	if (retdDatum)
		*retdDatum = dDatum;

	return true;
	}
Пример #15
0
bool CDatum::CreateBinary (IByteStream &Stream, int iSize, CDatum *retDatum)

//	CreateBinary
//
//	Creates a string datum containing binary data from the stream.
//	If iSize is -1 then we read as much as the stream has.
//	Otherwise  we read up to iSize.

	{
	//	LATER: Handle streams more than 2GB. Instead of asking how much space
	//	is left, maybe we should just ask to truncate the size that we're
	//	requesting.
	int iDataRemaining = Stream.GetStreamLength() - Stream.GetPos();
	int iBinarySize = (iSize < 0 ? iDataRemaining : Min(iDataRemaining, iSize));

	//	0-size

	if (iBinarySize == 0)
		{
		*retDatum = CDatum();
		return true;
		}

	//	Read the stream

	CComplexBinary *pBinary;
	try
		{
		pBinary = new CComplexBinary(Stream, iBinarySize);
		}
	catch (...)
		{
		return false;
		}

	//	Done

	*retDatum = CDatum(pBinary);

	//	Done

	return true;
	}
Пример #16
0
CDatum::CDatum (Types iType)

//	CDatum constructor

	{
	switch (iType)
		{
		case typeNil:
			m_dwData = 0;
			break;

		case typeTrue:
			m_dwData = constTrue;
			break;

		case typeInteger32:
			*this = CDatum((int)0);
			break;

		case typeString:
			*this = CDatum(NULL_STR);
			break;

		case typeArray:
			*this = CDatum(new CComplexArray);
			break;

		case typeDouble:
			*this = CDatum((double)0.0);
			break;

		case typeStruct:
			*this = CDatum(new CComplexStruct);
			break;

		case typeDateTime:
			*this = CDatum(CDateTime(CDateTime::Now));
			break;

		case typeIntegerIP:
			*this = CDatum(CIPInteger(0));
			break;

		case typeInteger64:
			*this = CDatum((DWORDLONG)0);
			break;

		default:
			ASSERT(false);
			m_dwData = 0;
			break;
		}
	}
Пример #17
0
void CCryptosaurEngine::MsgValidateAuthToken (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgValidateAuthToken
//
//	Cryptosaur.validateAuthToken {authToken}

	{
	//	Any service can validate a token

	if (!ValidateMessage(Msg, pSecurityCtx, false))
		return;

	//	Get the key to sign with (the key is guaranteed to exist because we
	//	checked at boot time).

	CIPInteger *pAuthTokenKey = m_Keys.GetAt(KEY_CRYPTOSAUR_AUTH_TOKEN);

	//	Validate

	CDatum dData;
	if (!CCryptosaurInterface::ValidateAuthToken(Msg.dPayload.GetElement(0), *pAuthTokenKey, &dData))
		{
		SendMessageReply(MSG_REPLY_DATA, CDatum(), Msg);
		return;
		}

	//	A sandboxed authtoken is not valid outside of its scope.
	//	(But it is valid in admin services).

	const CString &sScope = dData.GetElement(FIELD_SCOPE);
	if (!sScope.IsEmpty() 
			&& pSecurityCtx
			&& !pSecurityCtx->IsNamespaceAccessible(sScope))
		{
		SendMessageReply(MSG_REPLY_DATA, CDatum(), Msg);
		return;
		}

	//	Return the data in the auth token

	SendMessageReply(MSG_REPLY_DATA, dData, Msg);
	}
Пример #18
0
bool CDatum::CreateIPInteger (const CIPInteger &Value, CDatum *retdDatum)

//	CreateIPInteger
//
//	Creates an IPInteger datum

	{
	CComplexInteger *pIPInt = new CComplexInteger(Value);
	*retdDatum = CDatum(pIPInt);
	return true;
	}
Пример #19
0
void CAeonEngine::MsgInsertNew (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgInsertNew
//
//	Aeon.insertNew {tableName} {rowPath} {data}
//
//	{rowPath} is an array with as many elements as the table dimensions

	{
	AEONERR error;

	const CString &sTable = Msg.dPayload.GetElement(0);
	CDatum dRowPath = Msg.dPayload.GetElement(1);
	CDatum dData = Msg.dPayload.GetElement(2);

	//	Make sure we are allowed access to this table

	if (!ValidateTableAccess(Msg, pSecurityCtx, sTable))
		return;

	//	If the table doesn't exist, then we can't continue

	CAeonTable *pTable;
	if (!FindTable(sTable, &pTable))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_UNKNOWN_TABLE, sTable), Msg);
		return;
		}

	//	Parse the path

	CString sError;
	CRowKey Path;
	if (!pTable->ParseDimensionPathForCreate(dRowPath, &Path, &sError))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg);
		return;
		}

	//	Insert

	if (error = pTable->Insert(Path, dData, true, &sError))
		{
		if (error == AEONERR_ALREADY_EXISTS)
			SendMessageReplyError(MSG_ERROR_ALREADY_EXISTS, sError, Msg);
		else
			SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, sError, Msg);
		return;
		}

	//	Done

	SendMessageReply(MSG_OK, CDatum(), Msg);
	}
Пример #20
0
CDatum CAeonView::DebugDump (void) const

//	DebugDump
//
//	Returns data about the view.

	{
	int i;

	CComplexStruct *pData = new CComplexStruct;

	pData->SetElement(FIELD_RECOVERY_FILESPEC, m_Recovery.GetFilespec());

	CComplexArray *pSegments = new CComplexArray;
	for (i = 0; i < m_Segments.GetCount(); i++)
		pSegments->Append(m_Segments[i]->DebugDump());

	pData->SetElement(FIELD_SEGMENTS, CDatum(pSegments));

	return CDatum(pData);
	}
Пример #21
0
bool CDatum::CreateBinaryFromHandoff (CStringBuffer &Buffer, CDatum *retDatum)

//	CreateBinaryFromHandoff
//
//	Creates a binary datum

	{
	CComplexBinary *pBinary = new CComplexBinary;
	pBinary->TakeHandoff(Buffer);
	*retDatum = CDatum(pBinary);
	return true;
	}
Пример #22
0
void CEsperEngine::MsgGetStatus (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgGetStatus
//
//	Arc.getStatus

	{
	//	Ask the connections for a status

	CEsperConnection::SStatus Status;
	m_Connections.GetStatus(&Status);

	//	Compose into a struct

	CComplexStruct *pResult = new CComplexStruct;
	pResult->SetElement(CString("Esper/connectionActive"), CDatum(Status.iActive));
	pResult->SetElement(CString("Esper/connectionCount"), CDatum(Status.iTotalObjects));
	pResult->SetElement(CString("Esper/connectionsIdle"), CDatum(Status.iIdle));
	pResult->SetElement(CString("Esper/connectionsReading"), CDatum(Status.iWaitingForRead));
	pResult->SetElement(CString("Esper/connectionsWriting"), CDatum(Status.iWaitingForWrite));

	//	Done

	m_pProcess->SendMessageReply(MSG_REPLY_DATA, CDatum(pResult), Msg);
	}
Пример #23
0
bool CUserInfoSession::UpdateLoginFailure (void)

//	UpdateLoginFailure
//
//	Update the user record with the most recent failure

	{
	//	Set our state

	m_iState = stateWaitingForFailureUpdate;

	//	Mutate the record to set the login date

	CComplexStruct *pRecord = new CComplexStruct;
	pRecord->SetElement(FIELD_LOGIN_FAILURE_COUNT, CDatum((int)1));

	CComplexStruct *pMutation = new CComplexStruct;
	pMutation->SetElement(FIELD_LAST_LOGIN_FAILURE_ON, MUTATE_DATE_MODIFIED);
	pMutation->SetElement(FIELD_LOGIN_FAILURE_COUNT, MUTATE_INCREMENT);

	//	Create a payload

	CComplexArray *pPayload = new CComplexArray;
	pPayload->Insert(USERS_TABLE_NAME);
	pPayload->Insert(m_sUsernameKey);
	pPayload->Insert(CDatum(pRecord));
	pPayload->Insert(CDatum(pMutation));

	//	Send message

	ISessionHandler::SendMessageCommand(ADDRESS_AEON_COMMAND,
			MSG_AEON_MUTATE,
			GenerateAddress(PORT_CRYPTOSAUR_COMMAND),
			CDatum(pPayload),
			MESSAGE_TIMEOUT);

	//	Expect reply

	return true;
	}
Пример #24
0
bool CDatum::CreateIPIntegerFromHandoff (CIPInteger &Value, CDatum *retdDatum)

//	CreateIPInteger
//
//	Creates an IPInteger by taking a handoff

	{
	CComplexInteger *pIPInt = new CComplexInteger;
	pIPInt->TakeHandoff(Value);

	*retdDatum = CDatum(pIPInt);
	return true;
	}
Пример #25
0
CDatum CEsperEngine::GetPortStatus (void) const

//	GetPortStatus
//
//	Returns a struct with well-known fields for status.

	{
	CDatum dStatus = CDatum(CDatum::typeStruct);
	dStatus.SetElement(FIELD_CLASS, STR_ESPER_ENGINE);
	dStatus.SetElement(FIELD_STATUS, NULL_STR);

	return dStatus;
	}
Пример #26
0
void CEsperEngine::SendMessageReplyOnWrite (CDatum dConnection, DWORD dwBytesTransferred, const SArchonMessage &OriginalMsg)

//	SendMessageReplyOnWrite
//
//	Reply with Esper.onWrite

	{
	CComplexArray *pPayload = new CComplexArray;
	pPayload->Insert(dwBytesTransferred);
	pPayload->Insert(dConnection);

	m_pProcess->SendMessageReply(MSG_ESPER_ON_WRITE, CDatum(pPayload), OriginalMsg);
	}
Пример #27
0
void CMnemosynthDb::GenerateEndpointList (CDatum *retdList)

//	GenerateEndpointList
//
//	Returns data about all endpoints

	{
	CSmartLock Lock(m_cs);
	int i;

	CComplexArray *pList = new CComplexArray;
	for (i = 0; i < m_Endpoints.GetCount(); i++)
		{
		CComplexStruct *pData = new CComplexStruct;
		pData->SetElement(FIELD_ID, m_Endpoints[i].sName);
		pData->SetElement(FIELD_SEQ_RECV, m_Endpoints[i].dwSeqRecv);
		pData->SetElement(FIELD_SEQ_SENT, m_Endpoints[i].dwSeqSent);

		pList->Append(CDatum(pData));
		}

	*retdList = CDatum(pList);
	}
Пример #28
0
bool CComplexDateTime::CreateFromString (const CString &sString, CDatum *retdDatum)

//	CreateFromString
//
//	Creates a datum from a string

	{
	CDateTime DateTime;
	if (!CreateFromString(sString, &DateTime))
		return false;

	*retdDatum = CDatum(DateTime);
	return true;
	}
Пример #29
0
CDatum CDatum::GetArrayElement (int iIndex) const

//	GetArrayElement
//
//	Gets the appropriate element

	{
	ASSERT(iIndex >= 0);

	switch (m_dwData & AEON_TYPE_MASK)
		{
		case AEON_TYPE_COMPLEX:
			{
			if (GetBasicType() == typeArray)
				return raw_GetComplex()->GetElement(iIndex);
			else
				return (iIndex == 0 ? *this : CDatum());
			}

		default:
			return (iIndex == 0 ? *this : CDatum());
		}
	}
Пример #30
0
void CAeonEngine::MsgDeleteTable (const SArchonMessage &Msg, const CHexeSecurityCtx *pSecurityCtx)

//	MsgDeleteTable
//
//	Aeon.deleteTable {tableName}

	{
	//	Parameters

	const CString &sTable = Msg.dPayload.GetElement(0);

	//	Make sure we are allowed access to this table

	if (!ValidateTableAccess(Msg, pSecurityCtx, sTable))
		return;

	//	Lock while we remove the table

	CSmartLock Lock(m_cs);

	//	If the table doesn't exist, then we can't continue

	int iTablePos;
	if (!m_Tables.FindPos(sTable, &iTablePos))
		{
		SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, strPattern(STR_ERROR_UNKNOWN_TABLE, sTable), Msg);
		return;
		}

	//	Remove the table from the array

	CAeonTable *pTable = m_Tables.GetValue(iTablePos);
	m_Tables.Delete(iTablePos);

	//	Clean up the table

	if (!pTable->Delete())
		Log(MSG_LOG_ERROR, STR_UNABLE_TO_DELETE_TABLE_FILES);

	delete pTable;

	//	We can unlock now

	Lock.Unlock();

	//	Done

	SendMessageReply(MSG_OK, CDatum(), Msg);
	}