1 //=============================================================================
2 //
3 //   File : libkviperlcore.cpp
4 //   Creation date : Tue Jul 13 13:03:31 2004 GMT by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC client distribution
7 //   Copyright (C) 2004-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 //   This program is FREE software. You can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License
11 //   as published by the Free Software Foundation; either version 2
12 //   of the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the HOPE that it will be USEFUL,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 //   See the GNU General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public License
20 //   along with this program. If not, write to the Free Software Foundation,
21 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24 
25 #include "KviModule.h"
26 #include "kvi_settings.h"
27 #include "KviLocale.h"
28 #include "kvi_out.h"
29 #include "KviWindow.h"
30 #include "KviApplication.h"
31 #include "KviKvsScript.h"
32 #include "KviKvsVariant.h"
33 #include "KviUserInput.h"
34 #include "KviPointerHashTable.h"
35 
36 #include <QByteArray>
37 
38 #ifdef DEBUG
39 #undef DEBUG
40 #endif
41 
42 // I MUST say that the perl embedding process is somewhat ugly :(
43 // First of all the man pages are somewhat unreadable even
44 // for a non-novice perl user. The writer of each page assumed
45 // that you have already read each other page...
46 // Also browsing the pages with "man" is obviously out of mind
47 // but this can be solved by looking up some html docs on the net.
48 // Embedding multiple interpreters isn't that hard (after you
49 // have read perlembed) but to start passing parameters
50 // around you have to read at least perlembed, perlguts, perlxs,..
51 // take a look at the perlinternals and have a good trip
52 // around the web to find some examples for the functions
53 // that aren't explained enough in the pages.
54 // It gets even more weird when you attempt to include
55 // some XS functions... (what the heck is boot_DynaLoader ?).
56 
57 // ... and I'm still convinced that I'm leaking memory with
58 // the perl values, but well ...
59 
60 // anyway, once you struggled for a couple of days with all that
61 // stuff then you start getting things done... and it rox :)
62 
63 // Note for kvirc4: perl embedding has changed between perl 5.8 and perl 5.10.
64 // This version should work nice with both, but warranty is 5.10-only.
65 
66 #ifdef COMPILE_PERL_SUPPORT
67 #include <EXTERN.h>
68 #include <perl.h>
69 #include <XSUB.h>
70 
71 #define NEED_eval_pv
72 #include "ppport.h"
73 
74 #include "KviKvsRunTimeContext.h"
75 
76 static KviKvsRunTimeContext * g_pCurrentKvsContext = nullptr;
77 static bool g_bExecuteQuiet = false;
78 static KviCString g_szLastReturnValue("");
79 static QStringList g_lWarningList;
80 
81 // this is why we can't have nice things -- part of perl 5.17.1+
82 #ifdef __cplusplus
83 #define dNOOP (void)0
84 #else
85 #define dNOOP extern int Perl___notused(void)
86 #endif
87 
88 #include "xs.inc"
89 #endif // COMPILE_PERL_SUPPORT
90 
91 // perl redefines bool :///
92 #ifdef bool
93 #undef bool
94 #endif
95 
96 #ifdef COMPILE_PERL_SUPPORT
97 
98 #include "perlcoreinterface.h"
99 
100 // This should be able to be rewritten in the form:
101 // static PerlInterpreter *m_pInterpreter
102 
103 // people ... are you mad ? ... what the heck is "my_perl" ?
104 #define my_perl m_pInterpreter
105 
106 class KviPerlInterpreter
107 {
108 public:
109 	KviPerlInterpreter(const QString & szContextName);
110 	~KviPerlInterpreter();
111 
112 protected:
113 	QString m_szContextName;
114 	PerlInterpreter * m_pInterpreter;
115 
116 public:
117 	bool init(); // if this fails then well.. :D
118 	void done();
119 	bool execute(const QString & szCode, QStringList & args, QString & szRetVal, QString & szError, QStringList & lWarnings);
contextName() const120 	const QString & contextName() const { return m_szContextName; };
121 protected:
122 	QString svToQString(SV * sv);
123 };
124 
KviPerlInterpreter(const QString & szContextName)125 KviPerlInterpreter::KviPerlInterpreter(const QString & szContextName)
126 {
127 	m_szContextName = szContextName;
128 	m_pInterpreter = nullptr;
129 }
130 
~KviPerlInterpreter()131 KviPerlInterpreter::~KviPerlInterpreter()
132 {
133 	done();
134 }
135 
136 // this kinda sux :(
137 // It SHOULD be mentioned somewhere that
138 // this function is in DynaLoader.a in the perl
139 // distribution and you MUST link it statically.
140 
141 // Update: it is no more needed as of perl 5.10, since it is
142 // included in the standard libperl interface.
143 
144 extern "C" void boot_DynaLoader(pTHX_ CV * cv);
145 
xs_init(pTHX)146 extern "C" void xs_init(pTHX)
147 {
148 	char * file = (char *)__FILE__;
149 	// boot up the DynaLoader
150 	newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
151 	// now bootstrap the KVIrc module
152 	// This stuff is simply cutted and pasted from xs.inc
153 	// since I don't really know if it's safe to call
154 	// something like
155 	//   CV * dummy;
156 	//   boot_KVIrc(aTHX,dummy);
157 	// ...
158 	newXS("KVIrc::echo", XS_KVIrc_echo, file);
159 	newXS("KVIrc::say", XS_KVIrc_say, file);
160 	newXS("KVIrc::warning", XS_KVIrc_warning, file);
161 	newXS("KVIrc::getLocal", XS_KVIrc_getLocal, file);
162 	newXS("KVIrc::setLocal", XS_KVIrc_setLocal, file);
163 	newXS("KVIrc::getGlobal", XS_KVIrc_getGlobal, file);
164 	newXS("KVIrc::setGlobal", XS_KVIrc_setGlobal, file);
165 	newXS("KVIrc::eval", XS_KVIrc_eval, file);
166 	newXS("KVIrc::internalWarning", XS_KVIrc_internalWarning, file);
167 }
168 
init()169 bool KviPerlInterpreter::init()
170 {
171 	if(m_pInterpreter)
172 		done();
173 	const char * daArgs[] = { "yo", "-e", "0", "-w" };
174 	m_pInterpreter = perl_alloc();
175 	if(!m_pInterpreter)
176 		return false;
177 	PERL_SET_CONTEXT(m_pInterpreter);
178 	PL_perl_destruct_level = 1;
179 	perl_construct(m_pInterpreter);
180 	perl_parse(m_pInterpreter, xs_init, 4, (char **)daArgs, nullptr);
181 	QString szInitCode;
182 
183 	// this part of the code seems to be unnecessary
184 	// even if it is created by the perl make process...
185 	//	"our %EXPORT_TAGS = ('all' => [qw(echo)]);\n"
186 	//	"our @EXPORT_OK = (qw(echo));\n"
187 	//	"our @EXPORT = qw();\n"
188 	// This is probably needed only if perl has to load
189 	// the XS through XSLoader ?
190 	// Maybe also the remaining part of the package
191 	// declaration could be dropped as well...
192 	// I just haven't tried :D
193 
194 	szInitCode = QString(
195 	                 "{\n"
196 	                 "package KVIrc;\n"
197 	                 "require Exporter;\n"
198 	                 "our @ISA = qw(Exporter);\n"
199 	                 "1;\n"
200 	                 "}\n"
201 	                 "$g_szContext = \"%1\";\n"
202 	                 "$g_bExecuteQuiet = 0;\n"
203 	                 "$SIG{__WARN__} = sub\n"
204 	                 "{\n"
205 	                 "	my($p,$f,$l,$x);\n"
206 	                 "	($p,$f,$l) = caller;\n"
207 	                 "	KVIrc::internalWarning(\"At line \".$l.\" of Perl code: \");\n"
208 	                 "	KVIrc::internalWarning(join(' ',@_));\n"
209 	                 "}\n")
210 	                 .arg(m_szContextName);
211 
212 	eval_pv(szInitCode.toUtf8().data(), false);
213 	return true;
214 }
215 
done()216 void KviPerlInterpreter::done()
217 {
218 	if(!m_pInterpreter)
219 		return;
220 	PERL_SET_CONTEXT(m_pInterpreter);
221 	PL_perl_destruct_level = 1;
222 	perl_destruct(m_pInterpreter);
223 	perl_free(m_pInterpreter);
224 	m_pInterpreter = nullptr;
225 }
226 
svToQString(SV * sv)227 QString KviPerlInterpreter::svToQString(SV * sv)
228 {
229 	QString ret = "";
230 	if(!sv)
231 		return ret;
232 	STRLEN len;
233 	char * ptr = SvPV(sv, len);
234 	if(ptr)
235 		ret = ptr;
236 	return ret;
237 }
238 
execute(const QString & szCode,QStringList & args,QString & szRetVal,QString & szError,QStringList & lWarnings)239 bool KviPerlInterpreter::execute(
240     const QString & szCode,
241     QStringList & args,
242     QString & szRetVal,
243     QString & szError,
244     QStringList & lWarnings)
245 {
246 	if(!m_pInterpreter)
247 	{
248 		szError = __tr2qs_ctx("Internal error: Perl interpreter not initialized", "perl");
249 		return false;
250 	}
251 
252 	g_lWarningList.clear();
253 
254 	QByteArray szUtf8 = szCode.toUtf8();
255 	PERL_SET_CONTEXT(m_pInterpreter);
256 
257 	// clear the _ array
258 	AV * pArgs = get_av("_", 1);
259 	SV * pArg = av_shift(pArgs);
260 	while(SvOK(pArg))
261 	{
262 		SvREFCNT_dec(pArg);
263 		pArg = av_shift(pArgs);
264 	}
265 
266 	if(args.count() > 0)
267 	{
268 		// set the args in the _ arry
269 		av_unshift(pArgs, (I32)args.count());
270 		int idx = 0;
271 		for(auto tmp : args)
272 		{
273 			QByteArray szVal = tmp.toUtf8();
274 			pArg = newSVpv(szVal.data(), tmp.length());
275 			if(!av_store(pArgs, idx, pArg))
276 				SvREFCNT_dec(pArg);
277 			idx++;
278 		}
279 	}
280 
281 	// call the code
282 	SV * pRet = eval_pv(szUtf8.data(), false);
283 
284 	// clear the _ array again
285 	pArgs = get_av("_", 1);
286 	pArg = av_shift(pArgs);
287 	while(SvOK(pArg))
288 	{
289 		SvREFCNT_dec(pArg);
290 		pArg = av_shift(pArgs);
291 	}
292 	av_undef(pArgs);
293 
294 	// get the ret value
295 	if(pRet)
296 	{
297 		if(SvOK(pRet))
298 			szRetVal = svToQString(pRet);
299 	}
300 
301 	if(!g_lWarningList.isEmpty())
302 		lWarnings = g_lWarningList;
303 
304 	// and the eventual error string
305 	pRet = get_sv("@", false);
306 	if(pRet)
307 	{
308 		if(SvOK(pRet))
309 		{
310 			szError = svToQString(pRet);
311 			if(!szError.isEmpty())
312 				return false;
313 		}
314 	}
315 
316 	return true;
317 }
318 
319 static KviPointerHashTable<QString, KviPerlInterpreter> * g_pInterpreters = nullptr;
320 
perlcore_get_interpreter(const QString & szContextName)321 static KviPerlInterpreter * perlcore_get_interpreter(const QString & szContextName)
322 {
323 	KviPerlInterpreter * i = g_pInterpreters->find(szContextName);
324 	if(i)
325 		return i;
326 	i = new KviPerlInterpreter(szContextName);
327 	if(!i->init())
328 	{
329 		delete i;
330 		return nullptr;
331 	}
332 	g_pInterpreters->replace(szContextName, i);
333 	return i;
334 }
335 
perlcore_destroy_interpreter(const QString & szContextName)336 static void perlcore_destroy_interpreter(const QString & szContextName)
337 {
338 	KviPerlInterpreter * i = g_pInterpreters->find(szContextName);
339 	if(!i)
340 		return;
341 	g_pInterpreters->remove(szContextName);
342 	i->done();
343 	delete i;
344 }
345 
perlcore_destroy_all_interpreters()346 static void perlcore_destroy_all_interpreters()
347 {
348 	KviPointerHashTableIterator<QString, KviPerlInterpreter> it(*g_pInterpreters);
349 
350 	while(it.current())
351 	{
352 		KviPerlInterpreter * i = it.current();
353 		i->done();
354 		delete i;
355 		++it;
356 	}
357 	g_pInterpreters->clear();
358 }
359 
360 #endif // COMPILE_PERL_SUPPORT
361 
perlcore_module_ctrl(KviModule *,const char * cmd,void * param)362 static bool perlcore_module_ctrl(KviModule *, const char * cmd, void * param)
363 {
364 #ifdef COMPILE_PERL_SUPPORT
365 	if(kvi_strEqualCS(cmd, KVI_PERLCORECTRLCOMMAND_EXECUTE))
366 	{
367 		KviPerlCoreCtrlCommand_execute * ex = (KviPerlCoreCtrlCommand_execute *)param;
368 		if(ex->uSize != sizeof(KviPerlCoreCtrlCommand_execute))
369 			return false;
370 		g_pCurrentKvsContext = ex->pKvsContext;
371 		g_bExecuteQuiet = ex->bQuiet;
372 		if(ex->szContext.isEmpty())
373 		{
374 			KviPerlInterpreter * m = new KviPerlInterpreter("temporary");
375 			if(!m->init())
376 			{
377 				delete m;
378 				return false;
379 			}
380 			ex->bExitOk = m->execute(ex->szCode, ex->lArgs, ex->szRetVal, ex->szError, ex->lWarnings);
381 			m->done();
382 			delete m;
383 		}
384 		else
385 		{
386 			KviPerlInterpreter * m = perlcore_get_interpreter(ex->szContext);
387 			ex->bExitOk = m->execute(ex->szCode, ex->lArgs, ex->szRetVal, ex->szError, ex->lWarnings);
388 		}
389 		return true;
390 	}
391 	if(kvi_strEqualCS(cmd, KVI_PERLCORECTRLCOMMAND_DESTROY))
392 	{
393 		KviPerlCoreCtrlCommand_destroy * de = (KviPerlCoreCtrlCommand_destroy *)param;
394 		if(de->uSize != sizeof(KviPerlCoreCtrlCommand_destroy))
395 			return false;
396 		perlcore_destroy_interpreter(de->szContext);
397 		return true;
398 	}
399 #endif // COMPILE_PERL_SUPPORT
400 	return false;
401 }
402 
perlcore_module_init(KviModule *)403 static bool perlcore_module_init(KviModule *)
404 {
405 #ifdef COMPILE_PERL_SUPPORT
406 	g_pInterpreters = new KviPointerHashTable<QString, KviPerlInterpreter>(17, false);
407 	g_pInterpreters->setAutoDelete(false);
408 	int daArgc = 4;
409 	const char * daArgs[] = { "yo", "-e", "0", "-w" };
410 	char ** daEnv = nullptr;
411 	PERL_SYS_INIT3(&daArgc, (char ***)&daArgs, &daEnv);
412 	return true;
413 #else  // !COMPILE_PERL_SUPPORT
414 	return false;
415 #endif // !COMPILE_PERL_SUPPORT
416 }
417 
perlcore_module_cleanup(KviModule *)418 static bool perlcore_module_cleanup(KviModule *)
419 {
420 #ifdef COMPILE_PERL_SUPPORT
421 	perlcore_destroy_all_interpreters();
422 	delete g_pInterpreters;
423 	g_pInterpreters = nullptr;
424 // ifdef workaround for #842
425 #ifndef COMPILE_ON_MAC
426 	PERL_SYS_TERM();
427 #endif
428 #endif // COMPILE_PERL_SUPPORT
429 	return true;
430 }
431 
perlcore_module_can_unload(KviModule *)432 static bool perlcore_module_can_unload(KviModule *)
433 {
434 #ifdef COMPILE_PERL_SUPPORT
435 	return false;
436 	/* return (g_pInterpreters->count() == 0);
437 	 * Perlcore module can't be cleanly unloaded since PERL_SYS_INIT3
438 	 * and PERL_SYS_TERM should never be called more than once (#1105)
439 	 */
440 #endif // COMPILE_PERL_SUPPORT
441 	return true;
442 }
443 
444 KVIRC_MODULE(
445     "PerlCore",                                                     // module name
446     "4.0.0",                                                        // module version
447     "Copyright (C) 2008 Szymon Stefanek (pragma at kvirc dot net)", // author & (C)
448     "Perl scripting engine core",
449     perlcore_module_init,
450     perlcore_module_can_unload,
451     perlcore_module_ctrl,
452     perlcore_module_cleanup,
453     "perl")
454