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