1 #include "scriptingtcl.h"
2 #include "common/global.h"
3 #include "common/unused.h"
4 #include "db/db.h"
5 #include "parser/lexer.h"
6 #include "parser/token.h"
7 #include "common/utils_sql.h"
8 #include <QDebug>
9 #include <QMutexLocker>
10 
ScriptingTcl()11 ScriptingTcl::ScriptingTcl()
12 {
13     mainInterpMutex = new QMutex();
14 }
15 
~ScriptingTcl()16 ScriptingTcl::~ScriptingTcl()
17 {
18     safe_delete(mainInterpMutex);
19 }
20 
init()21 bool ScriptingTcl::init()
22 {
23     Q_INIT_RESOURCE(scriptingtcl);
24     QMutexLocker locker(mainInterpMutex);
25     mainContext = new ContextTcl();
26     return true;
27 }
28 
deinit()29 void ScriptingTcl::deinit()
30 {
31     QMutexLocker locker(mainInterpMutex);
32     safe_delete(mainContext);
33     Tcl_Finalize();
34     Q_CLEANUP_RESOURCE(scriptingtcl);
35 }
36 
getLanguage() const37 QString ScriptingTcl::getLanguage() const
38 {
39     return "Tcl";
40 }
41 
createContext()42 ScriptingPlugin::Context* ScriptingTcl::createContext()
43 {
44     ContextTcl* ctx = new ContextTcl();
45     contexts << ctx;
46     return ctx;
47 }
48 
releaseContext(ScriptingPlugin::Context * context)49 void ScriptingTcl::releaseContext(ScriptingPlugin::Context* context)
50 {
51     ContextTcl* ctx = getContext(context);
52     if (!ctx)
53         return;
54 
55     contexts.removeOne(ctx);
56     delete ctx;
57 }
58 
resetContext(ScriptingPlugin::Context * context)59 void ScriptingTcl::resetContext(ScriptingPlugin::Context* context)
60 {
61     ContextTcl* ctx = getContext(context);
62     if (!ctx)
63         return;
64 
65     ctx->reset();
66 }
67 
setVariable(ScriptingPlugin::Context * context,const QString & name,const QVariant & value)68 void ScriptingTcl::setVariable(ScriptingPlugin::Context* context, const QString& name, const QVariant& value)
69 {
70     ContextTcl* ctx = getContext(context);
71     if (!ctx)
72         return;
73 
74     setVariable(ctx->interp, name, value);
75 }
76 
getVariable(ScriptingPlugin::Context * context,const QString & name)77 QVariant ScriptingTcl::getVariable(ScriptingPlugin::Context* context, const QString& name)
78 {
79     ContextTcl* ctx = getContext(context);
80     if (!ctx)
81         return QVariant();
82 
83     return getVariable(ctx->interp, name);
84 }
85 
hasError(ScriptingPlugin::Context * context) const86 bool ScriptingTcl::hasError(ScriptingPlugin::Context* context) const
87 {
88     ContextTcl* ctx = getContext(context);
89     if (!ctx)
90         return false;
91 
92     return !ctx->error.isEmpty();
93 }
94 
getErrorMessage(ScriptingPlugin::Context * context) const95 QString ScriptingTcl::getErrorMessage(ScriptingPlugin::Context* context) const
96 {
97     ContextTcl* ctx = getContext(context);
98     if (!ctx)
99         return QString();
100 
101     return ctx->error;
102 }
103 
getIconPath() const104 QString ScriptingTcl::getIconPath() const
105 {
106     return ":/scriptingtcl/scriptingtcl.png";
107 }
108 
evaluate(ScriptingPlugin::Context * context,const QString & code,const QList<QVariant> & args,Db * db,bool locking)109 QVariant ScriptingTcl::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
110 {
111     ContextTcl* ctx = getContext(context);
112     if (!ctx)
113         return QVariant();
114 
115     setArgs(ctx, args);
116     return compileAndEval(ctx, code, db, locking);
117 }
118 
evaluate(const QString & code,const QList<QVariant> & args,Db * db,bool locking,QString * errorMessage)119 QVariant ScriptingTcl::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage)
120 {
121     QMutexLocker locker(mainInterpMutex);
122     setArgs(mainContext, args);
123     QVariant results = compileAndEval(mainContext, code, db, locking);
124 
125     if (errorMessage && !mainContext->error.isEmpty())
126         *errorMessage = mainContext->error;
127 
128     return results;
129 }
130 
getContext(ScriptingPlugin::Context * context) const131 ScriptingTcl::ContextTcl* ScriptingTcl::getContext(ScriptingPlugin::Context* context) const
132 {
133     ContextTcl* ctx = dynamic_cast<ContextTcl*>(context);
134     if (!ctx)
135         qDebug() << "Invalid context passed to ScriptingTcl:" << context;
136 
137     return ctx;
138 }
139 
compileAndEval(ScriptingTcl::ContextTcl * ctx,const QString & code,Db * db,bool locking)140 QVariant ScriptingTcl::compileAndEval(ScriptingTcl::ContextTcl* ctx, const QString& code, Db* db, bool locking)
141 {
142     ScriptObject* scriptObj = nullptr;
143     if (!ctx->scriptCache.contains(code))
144     {
145         scriptObj = new ScriptObject(code);
146         ctx->scriptCache.insert(code, scriptObj);
147     }
148     else
149     {
150         scriptObj = ctx->scriptCache[code];
151     }
152     Tcl_ResetResult(ctx->interp);
153     ctx->error.clear();
154 
155     ctx->db = db;
156     ctx->useDbLocking = locking;
157 
158     int result = Tcl_EvalObjEx(ctx->interp, scriptObj->getTclObj(), TCL_EVAL_GLOBAL);
159 
160     ctx->db = nullptr;
161     ctx->useDbLocking = false;
162 
163     if (result != TCL_OK)
164     {
165         ctx->error = QString::fromUtf8(Tcl_GetStringResult(ctx->interp));
166         return QVariant();
167     }
168     return extractResult(ctx);
169 }
170 
extractResult(ScriptingTcl::ContextTcl * ctx)171 QVariant ScriptingTcl::extractResult(ScriptingTcl::ContextTcl* ctx)
172 {
173     Tcl_Obj* obj = Tcl_GetObjResult(ctx->interp);
174     return tclObjToVariant(obj);
175 }
176 
setArgs(ScriptingTcl::ContextTcl * ctx,const QList<QVariant> & args)177 void ScriptingTcl::setArgs(ScriptingTcl::ContextTcl* ctx, const QList<QVariant>& args)
178 {
179     setVariable(ctx, "argc", args.size());
180     setVariable(ctx, "argv", args);
181 }
182 
argsToList(const QList<QVariant> & args)183 Tcl_Obj* ScriptingTcl::argsToList(const QList<QVariant>& args)
184 {
185     Tcl_Obj** objArray = new Tcl_Obj*[args.size()];
186 
187     int i = 0;
188     for (const QVariant& arg : args)
189         objArray[i++] = variantToTclObj(arg);
190 
191     Tcl_Obj* obj = Tcl_NewListObj(args.size(), objArray);
192     delete[] objArray;
193 
194     return obj;
195 }
196 
tclObjToVariant(Tcl_Obj * obj)197 QVariant ScriptingTcl::tclObjToVariant(Tcl_Obj* obj)
198 {
199     static const QStringList typeLiterals = {"boolean", "booleanString", "double", "int", "wideInt", "bignum", "bytearray", "string", "list", "dict"};
200 
201     TclDataType type = TclDataType::UNKNOWN;
202     if (obj->typePtr)
203     {
204         int typeIdx = typeLiterals.indexOf(obj->typePtr->name);
205         if (typeIdx > -1)
206             type = static_cast<TclDataType>(typeIdx);
207     }
208 
209     QVariant result;
210     bool ok = true;
211     switch (type)
212     {
213         case TclDataType::Boolean:
214         case TclDataType::BooleanString:
215         {
216             int b;
217             if (Tcl_GetBooleanFromObj(nullptr, obj, &b) == TCL_OK)
218                 result = (bool)b;
219             else
220                 ok = false;
221 
222             break;
223         }
224         case TclDataType::Double:
225         {
226             double d;
227             if (Tcl_GetDoubleFromObj(nullptr, obj, &d) == TCL_OK)
228                 result = d;
229             else
230                 ok = false;
231 
232             break;
233         }
234         case TclDataType::Int:
235         {
236             int i;
237             if (Tcl_GetIntFromObj(nullptr, obj, &i) == TCL_OK)
238                 result = i;
239             else
240                 ok = false;
241 
242             break;
243         }
244         case TclDataType::WideInt:
245         {
246             Tcl_WideInt wideInt;
247             if (Tcl_GetWideIntFromObj(nullptr, obj, &wideInt) == TCL_OK)
248                 result = (qint64)wideInt;
249             else
250                 ok = false;
251 
252             break;
253         }
254         case TclDataType::Bytearray:
255         {
256             int lgt;
257             unsigned char* bytes = Tcl_GetByteArrayFromObj(obj, &lgt);
258             result = QByteArray::fromRawData(reinterpret_cast<char*>(bytes), lgt);
259             break;
260         }
261         case TclDataType::List:
262         {
263             QList<QVariant> list;
264             int objc;
265             Tcl_Obj** objv = nullptr;
266             Tcl_ListObjGetElements(nullptr, obj, &objc, &objv);
267             for (int i = 0; i < objc; i++)
268                 list << tclObjToVariant(objv[i]);
269 
270             result = list;
271             break;
272         }
273         case TclDataType::Dict:
274         {
275             Tcl_DictSearch search;
276             Tcl_Obj* key = nullptr;
277             Tcl_Obj* value = nullptr;
278             QString keyStr;
279             QVariant valueVariant;
280             int done;
281             QHash<QString,QVariant> hash;
282             if (Tcl_DictObjFirst(nullptr, obj, &search, &key, &value, &done) == TCL_OK)
283             {
284                 for (; !done ; Tcl_DictObjNext(&search, &key, &value, &done))
285                 {
286                     keyStr = QString::fromUtf8(Tcl_GetStringFromObj(key, nullptr));
287                     valueVariant = tclObjToVariant(value);
288                     hash[keyStr] = valueVariant;
289                 }
290                 Tcl_DictObjDone(&search);
291             }
292             result = hash;
293             break;
294         }
295         case TclDataType::Bignum:
296         case TclDataType::String:
297         case TclDataType::UNKNOWN:
298         default:
299             result = tclObjToString(obj);
300             break;
301     }
302 
303     if (!ok)
304         result = tclObjToString(obj);
305 
306     return result;
307 }
308 
tclObjToString(Tcl_Obj * obj)309 QString ScriptingTcl::tclObjToString(Tcl_Obj* obj)
310 {
311     return QString::fromUtf8(Tcl_GetStringFromObj(obj, nullptr));
312 }
313 
variantToTclObj(const QVariant & value)314 Tcl_Obj* ScriptingTcl::variantToTclObj(const QVariant& value)
315 {
316     Tcl_Obj* obj = nullptr;
317     switch (value.type())
318     {
319         case QVariant::Bool:
320             obj = Tcl_NewBooleanObj(value.toBool());
321             break;
322         case QVariant::Int:
323         case QVariant::UInt:
324             obj = Tcl_NewIntObj(value.toInt());
325             break;
326         case QVariant::LongLong:
327         case QVariant::ULongLong:
328             obj = Tcl_NewWideIntObj((Tcl_WideInt)value.toLongLong());
329             break;
330         case QVariant::Double:
331             obj = Tcl_NewDoubleObj(value.toDouble());
332             break;
333         case QVariant::ByteArray:
334         {
335             QByteArray bytes = value.toByteArray();
336             unsigned char* ubytes = reinterpret_cast<unsigned char*>(bytes.data());
337             obj = Tcl_NewByteArrayObj(ubytes, bytes.size());
338             break;
339         }
340         case QVariant::List:
341         {
342             QList<QVariant> list = value.toList();
343             int listSize = list.size();
344             Tcl_Obj** objList = new Tcl_Obj*[listSize];
345             for (int i = 0; i < listSize; ++i)
346                 objList[i] = variantToTclObj(list[i]);
347 
348             obj = Tcl_NewListObj(listSize, objList);
349             delete[] objList;
350             break;
351         }
352         case QVariant::StringList:
353         {
354             QStringList list = value.toStringList();
355             int listSize = list.size();
356             Tcl_Obj** objList = new Tcl_Obj*[listSize];
357             for (int i = 0; i < listSize; ++i)
358                 objList[i] = stringToTclObj(list[i]);
359 
360             obj = Tcl_NewListObj(listSize, objList);
361             delete[] objList;
362             break;
363         }
364         case QVariant::Hash:
365         {
366             QHash<QString, QVariant> hash = value.toHash();
367             obj = Tcl_NewDictObj();
368             QHashIterator<QString, QVariant> it(hash);
369             while (it.hasNext())
370             {
371                 it.next();
372                 Tcl_DictObjPut(nullptr, obj, variantToTclObj(it.key()), variantToTclObj(it.value()));
373             }
374             break;
375         }
376         case QVariant::Map:
377         {
378             QMap<QString, QVariant> map = value.toMap();
379             obj = Tcl_NewDictObj();
380             QMapIterator<QString, QVariant> it(map);
381             while (it.hasNext())
382             {
383                 it.next();
384                 Tcl_DictObjPut(nullptr, obj, variantToTclObj(it.key()), variantToTclObj(it.value()));
385             }
386             break;
387         }
388         case QVariant::String:
389         default:
390             obj = stringToTclObj(value.toString());
391             break;
392     }
393 
394     if (!obj)
395         obj = stringToTclObj(value.toString());
396 
397     return obj;
398 }
399 
stringToTclObj(const QString & value)400 Tcl_Obj* ScriptingTcl::stringToTclObj(const QString& value)
401 {
402     return Tcl_NewStringObj(value.toUtf8().constData(), -1);
403 }
404 
dbCommand(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])405 int ScriptingTcl::dbCommand(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[])
406 {
407     ContextTcl* ctx = reinterpret_cast<ContextTcl*>(clientData);
408 
409     Tcl_Obj* result = nullptr;
410     if (!ctx->db)
411     {
412         result = Tcl_NewStringObj(tr("No database available in current context, while called Tcl's '%1' command.").arg("db").toUtf8().constData(), -1);
413         Tcl_SetObjResult(interp, result);
414         return TCL_ERROR;
415     }
416 
417     if (strcmp(Tcl_GetStringFromObj(objv[1], nullptr), "eval") == 0)
418     {
419         if (objc == 3)
420             return dbEval(ctx, interp, objv);
421         else if (objc == 5) {
422             return dbEvalRowByRow(ctx, interp, objv);
423         }
424     }
425     else if (strcmp(Tcl_GetStringFromObj(objv[1], nullptr), "rows") == 0 && objc == 3)
426     {
427         return dbEvalDeepResults(ctx, interp, objv);
428     }
429     else if (strcmp(Tcl_GetStringFromObj(objv[1], nullptr), "onecolumn") == 0 && objc == 3)
430     {
431         return dbEvalOneColumn(ctx, interp, objv);
432     }
433 
434     result = Tcl_NewStringObj(tr("Invalid '%1' command sytax. Should be: %2").arg("db", "db eval sql").toUtf8().constData(), -1);
435     Tcl_SetObjResult(interp, result);
436     return TCL_ERROR;
437 }
438 
initTclCommand(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])439 int ScriptingTcl::initTclCommand(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[])
440 {
441     UNUSED(clientData);
442     UNUSED(objv);
443 
444     if (objc > 1)
445     {
446         Tcl_Obj* result = Tcl_NewStringObj(tr("Error from Tcl's' '%1' command: %2").arg("tcl_init", "invalid # args: tcl_init").toUtf8().constData(), -1);
447         Tcl_SetObjResult(interp, result);
448         return TCL_ERROR;
449     }
450 
451     int res = Tcl_Init(interp);
452     if (res != TCL_OK)
453     {
454         ScriptObject codeObj("set tcl_library $tcl_pkgPath");
455         Tcl_EvalObjEx(interp, codeObj.getTclObj(), TCL_EVAL_GLOBAL);
456         res = Tcl_Init(interp);
457     }
458     return res;
459 }
460 
dbEval(ContextTcl * ctx,Tcl_Interp * interp,Tcl_Obj * const objv[])461 int ScriptingTcl::dbEval(ContextTcl* ctx, Tcl_Interp* interp, Tcl_Obj* const objv[])
462 {
463     SqlQueryPtr execResults = dbCommonEval(ctx, interp, objv);
464     if (execResults->isError())
465         return TCL_ERROR;
466 
467     Tcl_Obj* result = nullptr;
468     QList<QVariant> cells;
469     SqlResultsRowPtr row;
470     while (execResults->hasNext())
471     {
472         row = execResults->next();
473         cells += row->valueList();
474     }
475     result = variantToTclObj(cells);
476 
477     Tcl_SetObjResult(interp, result);
478     return TCL_OK;
479 }
480 
dbEvalRowByRow(ContextTcl * ctx,Tcl_Interp * interp,Tcl_Obj * const objv[])481 int ScriptingTcl::dbEvalRowByRow(ContextTcl* ctx, Tcl_Interp* interp, Tcl_Obj* const objv[])
482 {
483     SqlQueryPtr execResults = dbCommonEval(ctx, interp, objv);
484     if (execResults->isError())
485         return TCL_ERROR;
486 
487     Tcl_Obj* code = objv[4];
488     QString arrayName = tclObjToString(objv[3]);
489     const char* arrayCharName = arrayName.toUtf8().constData();
490     SqlResultsRowPtr row;
491     int resCode = TCL_OK;
492     QHash<QString, QVariant> valueMap;
493     while (execResults->hasNext())
494     {
495         row = execResults->next();
496 
497         Tcl_UnsetVar2(interp, arrayCharName, nullptr, 0);
498         valueMap = row->valueMap();
499         valueMap["*"] = QStringList(valueMap.keys());
500         if (setArrayVariable(interp, arrayName, valueMap) != TCL_OK)
501             return TCL_ERROR;
502 
503         resCode = Tcl_EvalObjEx(interp, code, 0);
504 
505         if (resCode == TCL_ERROR)
506             return TCL_ERROR;
507         else if (resCode == TCL_BREAK)
508             break;
509         else if (resCode == TCL_RETURN)
510             return TCL_RETURN;
511     }
512 
513     return TCL_OK;
514 }
515 
dbEvalDeepResults(ContextTcl * ctx,Tcl_Interp * interp,Tcl_Obj * const objv[])516 int ScriptingTcl::dbEvalDeepResults(ContextTcl* ctx, Tcl_Interp* interp, Tcl_Obj* const objv[])
517 {
518     SqlQueryPtr execResults = dbCommonEval(ctx, interp, objv);
519     if (execResults->isError())
520         return TCL_ERROR;
521 
522     Tcl_Obj* result = nullptr;
523     QList<QVariant> rows;
524     SqlResultsRowPtr row;
525     while (execResults->hasNext())
526     {
527         row = execResults->next();
528         rows << QVariant(row->valueList());
529     }
530     result = variantToTclObj(rows);
531 
532     Tcl_SetObjResult(interp, result);
533     return TCL_OK;
534 }
535 
dbEvalOneColumn(ScriptingTcl::ContextTcl * ctx,Tcl_Interp * interp,Tcl_Obj * const objv[])536 int ScriptingTcl::dbEvalOneColumn(ScriptingTcl::ContextTcl* ctx, Tcl_Interp* interp, Tcl_Obj* const objv[])
537 {
538     SqlQueryPtr execResults = dbCommonEval(ctx, interp, objv);
539     if (execResults->isError())
540         return TCL_ERROR;
541 
542     Tcl_Obj* result = nullptr;
543     QVariant resultValue;
544     if (execResults->hasNext())
545         resultValue = execResults->getSingleCell();
546 
547     result = variantToTclObj(resultValue);
548 
549     Tcl_SetObjResult(interp, result);
550     return TCL_OK;
551 }
552 
dbCommonEval(ContextTcl * ctx,Tcl_Interp * interp,Tcl_Obj * const objv[])553 SqlQueryPtr ScriptingTcl::dbCommonEval(ContextTcl* ctx, Tcl_Interp* interp, Tcl_Obj* const objv[])
554 {
555     Db::Flags flags;
556     if (!ctx->useDbLocking)
557         flags |= Db::Flag::NO_LOCK;
558 
559     Tcl_Obj* result = nullptr;
560     QString sql = QString::fromUtf8(Tcl_GetStringFromObj(objv[2], nullptr));
561 
562     TokenList bindTokens = Lexer::tokenize(sql).filter(Token::BIND_PARAM);
563     QString bindVarName;
564     QHash<QString, QVariant> queryArgs;
565     for (const TokenPtr& token : bindTokens)
566     {
567         bindVarName = getBindTokenName(token);
568         if (bindVarName == "?")
569             continue;
570 
571         queryArgs[token->value] = getVariable(interp, bindVarName);
572     }
573 
574     SqlQueryPtr execResults = ctx->db->exec(sql, queryArgs, flags);
575     if (execResults->isError())
576     {
577         result = Tcl_NewStringObj(tr("Error from Tcl's' '%1' command: %2").arg("db", execResults->getErrorText()).toUtf8().constData(), -1);
578         Tcl_SetObjResult(interp, result);
579     }
580     return execResults;
581 }
582 
setArrayVariable(Tcl_Interp * interp,const QString & arrayName,const QHash<QString,QVariant> & hash)583 int ScriptingTcl::setArrayVariable(Tcl_Interp* interp, const QString& arrayName, const QHash<QString, QVariant>& hash)
584 {
585     Tcl_Obj* varName = Tcl_NewStringObj(arrayName.toUtf8().constData(), -1);
586     Tcl_IncrRefCount(varName);
587 
588     Tcl_Obj* key = nullptr;
589     Tcl_Obj* value = nullptr;
590     Tcl_Obj* res = nullptr;
591 
592     QHashIterator<QString, QVariant> it(hash);
593     while (it.hasNext())
594     {
595         it.next();
596         key = variantToTclObj(it.key());
597         value = variantToTclObj(it.value());
598 
599         Tcl_IncrRefCount(key);
600         Tcl_IncrRefCount(value);
601 
602         res = Tcl_ObjSetVar2(interp, varName, key, value, 0);
603 
604         Tcl_DecrRefCount(key);
605         Tcl_DecrRefCount(value);
606 
607         if (!res)
608             return TCL_ERROR;
609     }
610     return TCL_OK;
611 }
612 
setVariable(Tcl_Interp * interp,const QString & name,const QVariant & value)613 void ScriptingTcl::setVariable(Tcl_Interp* interp, const QString& name, const QVariant& value)
614 {
615     Tcl_Obj* varName = Tcl_NewStringObj(name.toUtf8().constData(), -1);
616     Tcl_IncrRefCount(varName);
617     Tcl_Obj* tclObjValue = variantToTclObj(value);
618     Tcl_IncrRefCount(tclObjValue);
619     Tcl_ObjSetVar2(interp, varName, nullptr, tclObjValue, 0);
620     Tcl_DecrRefCount(tclObjValue);
621     Tcl_DecrRefCount(varName);
622 }
623 
getVariable(Tcl_Interp * interp,const QString & name)624 QVariant ScriptingTcl::getVariable(Tcl_Interp* interp, const QString& name)
625 {
626     Tcl_Obj* varName = Tcl_NewStringObj(name.toUtf8().constData(), -1);
627     Tcl_IncrRefCount(varName);
628     Tcl_Obj* obj = Tcl_ObjGetVar2(interp, varName, nullptr, 0);
629     if (!obj)
630         return QVariant();
631 
632     Tcl_IncrRefCount(obj);
633     QVariant val = tclObjToVariant(obj);
634     Tcl_DecrRefCount(varName);
635     Tcl_DecrRefCount(obj);
636     return val;
637 }
638 
ScriptObject(const QString & code)639 ScriptingTcl::ScriptObject::ScriptObject(const QString& code)
640 {
641     QByteArray utf8Bytes = code.toUtf8();
642     obj = Tcl_NewStringObj(utf8Bytes.constData(), utf8Bytes.size());
643     Tcl_IncrRefCount(obj);
644 }
645 
~ScriptObject()646 ScriptingTcl::ScriptObject::~ScriptObject()
647 {
648     Tcl_DecrRefCount(obj);
649 }
650 
getTclObj()651 Tcl_Obj* ScriptingTcl::ScriptObject::getTclObj()
652 {
653     return obj;
654 }
655 
ContextTcl()656 ScriptingTcl::ContextTcl::ContextTcl()
657 {
658     scriptCache.setMaxCost(cacheSize);
659     interp = Tcl_CreateInterp();
660     init();
661 }
662 
~ContextTcl()663 ScriptingTcl::ContextTcl::~ContextTcl()
664 {
665     Tcl_DeleteInterp(interp);
666 }
667 
reset()668 void ScriptingTcl::ContextTcl::reset()
669 {
670     Tcl_DeleteInterp(interp);
671     interp = Tcl_CreateInterp();
672     error = QString();
673     init();
674 }
675 
init()676 void ScriptingTcl::ContextTcl::init()
677 {
678     Tcl_CreateObjCommand(interp, "db", ScriptingTcl::dbCommand, reinterpret_cast<ClientData>(this), nullptr);
679     Tcl_CreateObjCommand(interp, "tcl_init", ScriptingTcl::initTclCommand, reinterpret_cast<ClientData>(this), nullptr);
680 }
681