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