summaryrefslogtreecommitdiffstats
path: root/src/kvirc/kvs/kvi_kvs_script.cpp
blob: d77919dcf255c3bf10126feced105be246d6cbea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
//=============================================================================
//
//   File : kvi_kvs_script.cpp
//   Creation date : Thu 25 Sep 2003 05.12 CEST by Szymon Stefanek
//
//   This file is part of the KVirc irc client distribution
//   Copyright (C) 2003 Szymon Stefanek (pragma at kvirc dot net)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your opinion) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#define __KVIRC__

#include "kvi_kvs_script.h"
#include "kvi_kvs_parser.h"
#include "kvi_kvs_report.h"
#include "kvi_kvs_runtimecontext.h"
#include "kvi_kvs_treenode_instruction.h"
#include "kvi_kvs_variantlist.h"
#include "kvi_kvs_kernel.h"

#include "kvi_locale.h"
#include "kvi_out.h"
#include "kvi_mirccntrl.h"
#include "kvi_window.h"
#include "kvi_app.h"

class KVIRC_API KviKvsScriptData
{
	friend class KviKvsScript;
protected:
	unsigned int                m_uRefs;     // Reference count for this structure

	TQString                     m_szName;    // script context name
	TQString                     m_szBuffer;  // NEVER TOUCH THIS
	const TQChar               * m_pBuffer;   // this points to m_szBuffer: use it to extract string data

	KviKvsScript::ScriptType    m_eType;     // the type of the code in m_szBuffer

	KviKvsTreeNodeInstruction * m_pTree;     // syntax tree
	unsigned int                m_uLock;     // this is increased while the script is being executed
};

//#warning "THERE IS SOME MESS WITH m_szBuffer and m_pBuffer : with some script copying we may get errors with negative char indexes!"

KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,ScriptType eType)
{
	m_pData = new KviKvsScriptData;
	m_pData->m_uRefs = 1;
	m_pData->m_szName = szName;
	m_pData->m_eType = eType;
	m_pData->m_szBuffer = szBuffer;
	if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = "";
	//KviTQString::detach(*(m_pData->m_pszBuffer));
	m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0
	m_pData->m_uLock = 0;
	m_pData->m_pTree = 0;
}

KviKvsScript::KviKvsScript(const TQString &szName,const TQString &szBuffer,KviKvsTreeNodeInstruction * pPreparsedTree,ScriptType eType)
{
	m_pData = new KviKvsScriptData;
	m_pData->m_uRefs = 1;
	m_pData->m_szName = szName;
	m_pData->m_szBuffer = szBuffer;
	m_pData->m_eType = eType;
	if(m_pData->m_szBuffer.isNull())m_pData->m_szBuffer = "";
	//KviTQString::detach(*(m_pData->m_pszBuffer));
	m_pData->m_pBuffer = KviTQString::nullTerminatedArray(m_pData->m_szBuffer); // never 0
	m_pData->m_uLock = 0;
	m_pData->m_pTree = pPreparsedTree;
}

KviKvsScript::KviKvsScript(const KviKvsScript &src)
{
	m_pData = src.m_pData;
	m_pData->m_uRefs++;
}

KviKvsScript::~KviKvsScript()
{
	if(m_pData->m_uRefs < 2)
	{
		if(m_pData->m_uLock)debug("WARNING: Destroying a locked KviKvsScript");
		if(m_pData->m_pTree)delete m_pData->m_pTree;
		delete m_pData;
	} else {
		m_pData->m_uRefs--;
	}
}

void KviKvsScript::setName(const TQString &szName)
{
	if(m_pData->m_uRefs > 1)detach();
	m_pData->m_szName = szName;
}

const TQString & KviKvsScript::name() const
{
	return m_pData->m_szName;
}

const TQString & KviKvsScript::code() const
{
	return m_pData->m_szBuffer;
}

bool KviKvsScript::locked() const
{
	return m_pData->m_uLock > 0;
}

void KviKvsScript::dump(const char * prefix)
{
	if(m_pData->m_pTree)m_pData->m_pTree->dump(prefix);
	else debug("%s KviKvsScript : no tree to dump",prefix);
}

void KviKvsScript::detach()
{
	if(m_pData->m_uRefs <= 1)return;
	m_pData->m_uRefs--;
	KviKvsScriptData * d = new KviKvsScriptData;
	d->m_uRefs = 1;
	d->m_eType = m_pData->m_eType;
	d->m_szBuffer = m_pData->m_szBuffer;
	if(d->m_szBuffer.isNull())d->m_szBuffer = "";
	KviTQString::detach(d->m_szBuffer);
	d->m_pBuffer = KviTQString::nullTerminatedArray(d->m_szBuffer); // never 0
	d->m_uLock = 0;
	d->m_pTree = 0;
	m_pData = d;
}

const TQChar * KviKvsScript::buffer() const
{
	return m_pData->m_pBuffer;
}

int KviKvsScript::run(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal)
{
	// static helper
	KviKvsScript s("kvirc::corecall(run)",szCode);
	return s.run(pWindow,pParams,pRetVal,PreserveParams);
}

int KviKvsScript::evaluate(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,KviKvsVariant * pRetVal)
{
	// static helper
	KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter);
	return s.run(pWindow,pParams,pRetVal,PreserveParams);
}

int KviKvsScript::evaluateAsString(const TQString &szCode,KviWindow * pWindow,KviKvsVariantList * pParams,TQString &szRetVal)
{
	// static helper
	KviKvsVariant ret;
	KviKvsScript s("kvirc::corecall(evalutate)",szCode,Parameter);
	int iRet = s.run(pWindow,pParams,&ret,PreserveParams);
	ret.asString(szRetVal);
	return iRet;
}

int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,TQString &szRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
	KviKvsVariant retVal;
	int iRet = run(pWnd,pParams,&retVal,iRunFlags,pExtData);
	retVal.asString(szRetVal);
	return iRet;
}

//static long int g_iTreeCacheHits = 0;
//static long int g_iTreeCacheMisses = 0;

int KviKvsScript::run(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
	if(!m_pData->m_pTree)
	{
		//g_iTreeCacheMisses++;
		//debug("CREATING TREE FOR SCRIPT %s",name().latin1());
		//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
		if(!parse(pWnd,iRunFlags))
		{
			if(pParams && !(iRunFlags & PreserveParams))delete pParams;
			return Error;
		}
	} else {
		//g_iTreeCacheHits++;
		//debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1());
		//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
	}
	
	return execute(pWnd,pParams,pRetVal,iRunFlags,pExtData);
}

int KviKvsScript::run(KviKvsRunTimeContext * pContext,int iRunFlags)
{
	if(!m_pData->m_pTree)
	{
		//g_iTreeCacheMisses++;
		//debug("CREATING TREE FOR SCRIPT %s",name().latin1());
		//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
		if(!parse(pContext->window(),iRunFlags))
			return Error;
	} else {
		//g_iTreeCacheHits++;
		//debug("USING A CACHED TREE FOR SCRIPT %s",name().latin1());
		//debug("TREE CACHE STATS: HITS=%d, MISSES=%d",g_iTreeCacheHits,g_iTreeCacheMisses);
	}

	int iRet;

	if(iRunFlags & Quiet)
	{
		bool bMustReEnable = !(pContext->reportingDisabled());
		pContext->disableReporting();
		iRet = executeInternal(pContext);
		if(bMustReEnable)pContext->enableReporting();
	} else {
		iRet = executeInternal(pContext);
	}
	
	return iRet;
}


bool KviKvsScript::parse(KviWindow * pOutput,int iRunFlags)
{
	if(m_pData->m_pTree)
	{
		// there is already a tree
		// if we have more than one ref, detach!
		if(m_pData->m_uRefs > 1)
		{
			// mmmh.. more than one ref! .. detach
			detach();
		} else {
			// only a single ref: we're the owner of the tree
			if(m_pData->m_uLock)
			{
				// ops... someone is locked in THIS script object
				debug("WARNING: Trying to reparse a locked KviKvsScript!");
				return false;
			}
			if(m_pData->m_pTree)delete m_pData->m_pTree;
			m_pData->m_pTree = 0;
		}
	} // else there is no tree at all, nobody can be locked inside

	KviKvsParser p(this,(iRunFlags & Quiet) ? 0 : pOutput);
	// parse never blocks
	
	int iFlags = iRunFlags & AssumeLocals ? KviKvsParser::AssumeLocals : 0;
	if(iRunFlags & Pedantic)iFlags |= KviKvsParser::Pedantic;
	
	switch(m_pData->m_eType)
	{
		case Expression:
			m_pData->m_pTree = p.parseAsExpression(m_pData->m_pBuffer,iFlags);
		break;
		case Parameter:
			m_pData->m_pTree = p.parseAsParameter(m_pData->m_pBuffer,iFlags);
		break;
		case InstructionList:
		default:
			m_pData->m_pTree = p.parse(m_pData->m_pBuffer,iFlags);
		break;
	}

	//debug("\n\nDUMPING SCRIPT");
	//dump("");
	//debug("END OF SCRIPT DUMP\n\n");

	return !p.error();
}


int KviKvsScript::executeInternal(KviKvsRunTimeContext * pContext)
{
	// lock this script
	m_pData->m_uLock++;
	
	int iRunStatus = Success;

	if(!m_pData->m_pTree->execute(pContext))
	{
		if(pContext->error())iRunStatus = Error;
		else {
			// else just a halt, return or sth like that
			if(pContext->haltCalled())
				iRunStatus |= HaltEncountered;
		}
	}
	
	// we can't block any longer: unlock
	m_pData->m_uLock--;

	return iRunStatus;
}

int KviKvsScript::execute(KviWindow * pWnd,KviKvsVariantList * pParams,KviKvsVariant * pRetVal,int iRunFlags,KviKvsExtendedRunTimeData * pExtData)
{
	bool bDeleteParams = !(iRunFlags & PreserveParams);

	// do we have a parsed tree ?
	if(!m_pData->m_pTree)
	{
		if(pParams && bDeleteParams)delete pParams;
		// this is intended for developers only
		pWnd->outputNoFmt(KVI_OUT_PARSERERROR,"[developer error]: you must succesfully call KviKvsScript::parse() before KviKvsScript::execute()");
		return Error;
	}
	// do we need to pass dummy params ?
	if(!pParams)
	{
		pParams = KviKvsKernel::instance()->emptyParameterList();
		bDeleteParams = false;
	}

	bool bDeleteRetVal = false;

	if(!pRetVal)
	{
		pRetVal = new KviKvsVariant();
		bDeleteRetVal = true;
	}

	KviKvsRunTimeContext ctx(this,pWnd,pParams,pRetVal,pExtData);
	
	if(iRunFlags & Quiet)
		ctx.disableReporting();
	
	int iRunStatus = executeInternal(&ctx);
	
	// don't forget to delete the params
	if(bDeleteParams)delete pParams;
	if(bDeleteRetVal)delete pRetVal;
    pParams = 0; pRetVal = 0;
	return iRunStatus;
}