Code::Blocks  SVN r11506
scriptingmanager.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 10769 $
6  * $Id: scriptingmanager.cpp 10769 2016-02-06 14:26:58Z mortenmacfly $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/sdk/scriptingmanager.cpp $
8  */
9 
10 #include <sdk_precomp.h>
11 
12 #ifndef CB_PRECOMP
13  #include "scriptingmanager.h"
14  #include "cbeditor.h"
15  #include "cbexception.h"
16  #include "configmanager.h"
17  #include "editormanager.h"
18  #include "globals.h"
19  #include "logmanager.h"
20  #include "manager.h"
21 
22  #include <settings.h>
23  #include <wx/msgdlg.h>
24  #include <wx/file.h>
25  #include <wx/filename.h>
26  #include <wx/regex.h>
27 #endif
28 
29 #include "crc32.h"
30 #include "menuitemsmanager.h"
32 #include "sqplus.h"
33 #include "scriptbindings.h"
34 #include "sc_plugin.h"
35 #include "sqstdstring.h"
36 
37 template<> ScriptingManager* Mgr<ScriptingManager>::instance = nullptr;
38 template<> bool Mgr<ScriptingManager>::isShutdown = false;
39 
42 
43 void PrintSquirrelToWxString(wxString& msg, const SQChar* s, va_list& vl)
44 {
45  int buffer_size = 2048;
46  SQChar* tmp_buffer;
47  for (;;buffer_size*=2)
48  {
49  tmp_buffer = new SQChar [buffer_size];
50  int retvalue = vsnprintf(tmp_buffer, buffer_size, s, vl);
51  if (retvalue < buffer_size)
52  {
53  // Buffersize was large enough
54  msg = cbC2U(tmp_buffer);
55  delete[] tmp_buffer;
56  break;
57  }
58  // Buffer size was not enough
59  delete[] tmp_buffer;
60  }
61 }
62 
63 static void ScriptsPrintFunc(HSQUIRRELVM /*v*/, const SQChar * s, ...)
64 {
65  va_list vl;
66  va_start(vl,s);
67  wxString msg;
68  PrintSquirrelToWxString(msg,s,vl);
69  va_end(vl);
70 
71  s_ScriptErrors << msg;
72 }
73 
74 static void CaptureScriptOutput(HSQUIRRELVM /*v*/, const SQChar * s, ...)
75 {
76  va_list vl;
77  va_start(vl,s);
78  wxString msg;
79  PrintSquirrelToWxString(msg,s,vl);
80  ::capture.append(msg);
81  va_end(vl);
82 }
83 
84 BEGIN_EVENT_TABLE(ScriptingManager, wxEvtHandler)
85 //
86 END_EVENT_TABLE()
87 
89  : m_AttachedToMainWindow(false),
90  m_MenuItemsManager(false) // not auto-clear
91 {
92  //ctor
93 
94  // initialize but don't load the IO lib
95  SquirrelVM::Init((SquirrelInitFlags)(sqifAll & ~sqifIO));
96 
97  if (!SquirrelVM::GetVMPtr())
98  cbThrow(_T("Can't create scripting engine!"));
99 
100  sq_setprintfunc(SquirrelVM::GetVMPtr(), ScriptsPrintFunc);
101  sqstd_register_stringlib(SquirrelVM::GetVMPtr());
102 
103  RefreshTrusts();
104 
105  // register types
107 }
108 
110 {
111  //dtor
112  // save trusted scripts set
114  int i = 0;
115  TrustedScripts::iterator it;
116  for (it = m_TrustedScripts.begin(); it != m_TrustedScripts.end(); ++it)
117  {
118  if (!it->second.permanent)
119  continue;
120  wxString key = wxString::Format(_T("trust%d"), i++);
121  wxString value = wxString::Format(_T("%s?%x"), it->first.c_str(), it->second.crc);
122  myMap.insert(myMap.end(), std::make_pair(key, value));
123  }
124  Manager::Get()->GetConfigManager(_T("security"))->Write(_T("/trusted_scripts"), myMap);
125 
126  SquirrelVM::Shutdown();
127 }
128 
130 {
131  // done in scriptbindings.cpp
132 }
133 
135 {
136 // wxCriticalSectionLocker c(cs);
137 
138  wxLogNull ln; // own error checking implemented -> avoid debug warnings
139 
140  wxString fname(filename);
141  wxFile f(fname); // try to open
142  if (!f.IsOpened())
143  {
144  bool found = false;
145 
146  // check in same dir as currently running script (if any)
148  {
149  fname = wxFileName(m_CurrentlyRunningScriptFile).GetPath() + _T('/') + filename;
150  f.Open(fname);
151  found = f.IsOpened();
152  }
153 
154  if (!found)
155  {
156  // check in standard script dirs
158  f.Open(fname);
159  if (!f.IsOpened())
160  {
161  Manager::Get()->GetLogManager()->DebugLog(_T("Can't open script ") + filename);
162  return false;
163  }
164  }
165  }
166  // read file
167  wxString contents = cbReadFileContents(f);
169  bool ret = LoadBuffer(contents, fname);
171  return ret;
172 }
173 
174 bool ScriptingManager::LoadBuffer(const wxString& buffer, const wxString& debugName)
175 {
176  // includes guard to avoid recursion
177  wxString incName = UnixFilename(debugName);
178  if (m_IncludeSet.find(incName) != m_IncludeSet.end())
179  {
180  Manager::Get()->GetLogManager()->LogWarning(F(_T("Ignoring Include(\"%s\") because it would cause recursion..."), incName.wx_str()));
181  return true;
182  }
183  m_IncludeSet.insert(incName);
184 
185 // wxCriticalSectionLocker c(cs);
186 
187  s_ScriptErrors.Clear();
188 
189  // compile script
190  SquirrelObject script;
191  try
192  {
193  script = SquirrelVM::CompileBuffer(cbU2C(buffer), cbU2C(debugName));
194  }
195  catch (SquirrelError e)
196  {
197  cbMessageBox(wxString::Format(_T("Filename: %s\nError: %s\nDetails: %s"), debugName.c_str(), cbC2U(e.desc).c_str(), s_ScriptErrors.c_str()), _("Script compile error"), wxICON_ERROR);
198  m_IncludeSet.erase(incName);
199  return false;
200  }
201 
202  // run script
203  try
204  {
205  SquirrelVM::RunScript(script);
206  }
207  catch (SquirrelError e)
208  {
209  cbMessageBox(wxString::Format(_T("Filename: %s\nError: %s\nDetails: %s"), debugName.c_str(), cbC2U(e.desc).c_str(), s_ScriptErrors.c_str()), _("Script run error"), wxICON_ERROR);
210  m_IncludeSet.erase(incName);
211  return false;
212  }
213  m_IncludeSet.erase(incName);
214  return true;
215 }
216 
217 
219 {
220 // wxCriticalSectionLocker c(cs);
221 
222  s_ScriptErrors.Clear();
223  ::capture.Clear();
224 
225  sq_setprintfunc(SquirrelVM::GetVMPtr(), CaptureScriptOutput);
226  bool res = LoadBuffer(buffer);
227  sq_setprintfunc(SquirrelVM::GetVMPtr(), ScriptsPrintFunc);
228 
229  return res ? ::capture : (wxString) wxEmptyString;
230 }
231 
232 wxString ScriptingManager::GetErrorString(SquirrelError* exception, bool clearErrors)
233 {
234  wxString msg;
235  if (exception)
236  msg << cbC2U(exception->desc);
237  msg << s_ScriptErrors;
238 
239  if (clearErrors)
240  s_ScriptErrors.Clear();
241 
242  return msg;
243 }
244 
245 void ScriptingManager::DisplayErrors(SquirrelError* exception, bool clearErrors)
246 {
247  wxString msg = GetErrorString(exception, clearErrors);
248  if (!msg.IsEmpty())
249  {
250  if (cbMessageBox(_("Script errors have occured...\nPress 'Yes' to see the exact errors."),
251  _("Script errors"),
253  {
254  GenericMultiLineNotesDlg dlg(Manager::Get()->GetAppWindow(),
255  _("Script errors"),
256  msg,
257  true);
258  dlg.ShowModal();
259  }
260  }
261 }
262 
264 {
265  s_ScriptErrors << output;
266 }
267 
269 {
270  return -1;
271 }
272 
274 {
275  // attach this event handler in the main window (one-time run)
277  {
278  Manager::Get()->GetAppWindow()->PushEventHandler(this);
279  m_AttachedToMainWindow = true;
280  }
281 
282  for (size_t i = 0; i < ids.GetCount(); ++i)
283  {
284  Connect(ids[i], -1, wxEVT_COMMAND_MENU_SELECTED,
285  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
287  }
288  return true;
289 }
290 
291 bool ScriptingManager::RegisterScriptMenu(const wxString& menuPath, const wxString& scriptOrFunc, bool isFunction)
292 {
293  // attach this event handler in the main window (one-time run)
295  {
296  Manager::Get()->GetAppWindow()->PushEventHandler(this);
297  m_AttachedToMainWindow = true;
298  }
299 
300  int id = wxNewId();
301  id = m_MenuItemsManager.CreateFromString(menuPath, id);
302  wxMenuItem* item = Manager::Get()->GetAppFrame()->GetMenuBar()->FindItem(id);
303  if (item)
304  {
305  if (!isFunction)
306  item->SetHelp(_("Press SHIFT while clicking this menu item to edit the assigned script in the editor"));
307 
308  Connect(id, -1, wxEVT_COMMAND_MENU_SELECTED,
309  (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
311 
312  MenuBoundScript mbs;
313  mbs.scriptOrFunc = scriptOrFunc;
314  mbs.isFunc = isFunction;
315  m_MenuIDToScript.insert(m_MenuIDToScript.end(), std::make_pair(id, mbs));
316  #if wxCHECK_VERSION(3, 0, 0)
317  Manager::Get()->GetLogManager()->Log(F(_("Script/function '%s' registered under menu '%s'"), scriptOrFunc.wx_str(), menuPath.wx_str()));
318  #else
319  Manager::Get()->GetLogManager()->Log(F(_("Script/function '%s' registered under menu '%s'"), scriptOrFunc.c_str(), menuPath.c_str()));
320  #endif
321 
322  return true;
323  }
324 
325  Manager::Get()->GetLogManager()->Log(_("Error registering script menu: ") + menuPath);
326  return false;
327 }
328 
329 bool ScriptingManager::UnRegisterScriptMenu(cb_unused const wxString& menuPath)
330 {
331  // TODO: not implemented
332  Manager::Get()->GetLogManager()->DebugLog(_T("ScriptingManager::UnRegisterScriptMenu() not implemented"));
333  return false;
334 }
335 
337 {
339  return true;
340 }
341 
343 {
344  TrustedScripts::iterator it = m_TrustedScripts.find(script);
345  if (it == m_TrustedScripts.end())
346  return false;
347  // check the crc too
348  wxUint32 crc = wxCrc32::FromFile(script);
349  if (crc == it->second.crc)
350  return true;
351  cbMessageBox(script + _T("\n\n") + _("The script was marked as \"trusted\" but it has been modified "
352  "since then.\nScript not trusted anymore."),
353  _("Warning"), wxICON_WARNING);
354  m_TrustedScripts.erase(it);
355  return false;
356 }
357 
359 {
361 }
362 
363 void ScriptingManager::TrustScript(const wxString& script, bool permanently)
364 {
365  // TODO: what should happen when script is empty()?
366 
367  TrustedScripts::iterator it = m_TrustedScripts.find(script);
368  if (it != m_TrustedScripts.end())
369  {
370  // already trusted, remove it from the trusts (we recreate the trust below)
371  m_TrustedScripts.erase(it);
372  }
373 
374  TrustedScriptProps props;
375  props.permanent = permanently;
376  props.crc = wxCrc32::FromFile(script);
377 
378  m_TrustedScripts.insert(m_TrustedScripts.end(), std::make_pair(script, props));
379 }
380 
382 {
384 }
385 
387 {
388  TrustedScripts::iterator it = m_TrustedScripts.find(script);
389  if (it != m_TrustedScripts.end())
390  {
391  // already trusted, remove it from the trusts (we recreate the trust below)
392  m_TrustedScripts.erase(it);
393  return true;
394  }
395  return false;
396 }
397 
399 {
400  // reload trusted scripts set
401  m_TrustedScripts.clear();
403  Manager::Get()->GetConfigManager(_T("security"))->Read(_T("/trusted_scripts"), &myMap);
404  ConfigManagerContainer::StringToStringMap::iterator it;
405  for (it = myMap.begin(); it != myMap.end(); ++it)
406  {
407  wxString key = it->second.BeforeFirst(_T('?'));
408  wxString value = it->second.AfterFirst(_T('?'));
409 
410  TrustedScriptProps props;
411  props.permanent = true;
412  unsigned long tmp;
413  value.ToULong(&tmp, 16);
414  props.crc = tmp;
415  m_TrustedScripts.insert(m_TrustedScripts.end(), std::make_pair(key, props));
416  }
417 }
418 
420 {
421  return m_TrustedScripts;
422 }
423 
425 {
426  MenuIDToScript::iterator it = m_MenuIDToScript.find(event.GetId());
427  if (it == m_MenuIDToScript.end())
428  {
429  cbMessageBox(_("No script associated with this menu?!?"), _("Error"), wxICON_ERROR);
430  return;
431  }
432 
433  MenuBoundScript& mbs = it->second;
434 
435  // is it a function?
436  if (mbs.isFunc)
437  {
438  try
439  {
440  SqPlus::SquirrelFunction<void> f(cbU2C(mbs.scriptOrFunc));
441  f();
442  }
443  catch (SquirrelError exception)
444  {
445  DisplayErrors(&exception);
446  }
447  return;
448  }
449 
450  // script loading below
451 
453  {
455  Manager::Get()->GetEditorManager()->Open(script);
456  return;
457  }
458 
459  // run script
460  try
461  {
462  if (!LoadScript(mbs.scriptOrFunc))
463  cbMessageBox(_("Could not run script: ") + mbs.scriptOrFunc, _("Error"), wxICON_ERROR);
464  }
465  catch (SquirrelError exception)
466  {
467  DisplayErrors(&exception);
468  }
469 }
470 
472 {
474 }
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
bool RegisterScriptMenu(const wxString &menuPath, const wxString &scriptOrFunc, bool isFunction)
Script-bound function to register a script with a menu item.
wxString m_CurrentlyRunningScriptFile
int wxNewId()
bool Open(const wxString &filename, wxFile::OpenMode mode=wxFile::read, int access=wxS_DEFAULT)
#define wxICON_WARNING
ConfigManager * GetConfigManager(const wxString &name_space) const
Definition: manager.cpp:474
static Manager * Get()
Use Manager::Get() to get a pointer to its instance Manager::Get() is guaranteed to never return an i...
Definition: manager.cpp:182
int Configure()
Configure scripting in Code::Blocks.
#define wxICON_ERROR
void LogWarning(const wxString &msg, int i=app_log)
Definition: logmanager.h:141
Scripts folder in base dir.
Definition: configmanager.h:80
void OnScriptPluginMenu(wxCommandEvent &event)
Scripts folder in user&#39;s dir.
Definition: configmanager.h:74
const TrustedScripts & GetTrustedScripts()
Access the script trusts container (const).
wxCStrData c_str() const
wxString & append(const wxString &str, size_t pos, size_t n)
#define wxNO_DEFAULT
#define _T(string)
bool IsScriptTrusted(const wxString &script)
Security function.
IncludeSet m_IncludeSet
#define wxYES_NO
virtual int CreateFromString(const wxString &menuPath, int id)
Create menu path from string.
bool ToULong(unsigned long *val, int base=10) const
void TrustScript(const wxString &script, bool permanently)
Security function to trust a script.
MenuItemsManager m_MenuItemsManager
void TrustCurrentlyRunningScript(bool permanently)
Security function to trust a script.
std::map< wxString, TrustedScriptProps > TrustedScripts
Script trusts container struct.
wxString AfterFirst(wxUniChar ch) const
static wxString LocateDataFile(const wxString &filename, int search_dirs=sdAllKnown)
Locate a file in an installation- and platform-independent way.
wxWindow * GetAppWindow() const
Definition: manager.cpp:424
EditorManager * GetEditorManager() const
Definition: manager.cpp:434
DLLIMPORT const wxWX2MBbuf cbU2C(const wxString &str)
Return multibyte (C string) representation of the string.
Definition: globals.cpp:743
wxString BeforeFirst(wxUniChar ch, wxString *rest=NULL) const
bool LoadScript(const wxString &filename)
Loads a script.
DLLIMPORT wxString cbReadFileContents(wxFile &file, wxFontEncoding encoding=wxFONTENCODING_SYSTEM)
Reads a wxString from a non-unicode file. File must be open. File is closed automatically.
Definition: globals.cpp:697
void Write(const wxString &name, const wxString &value, bool ignoreEmpty=false)
DLLIMPORT wxString UnixFilename(const wxString &filename, wxPathFormat format=wxPATH_NATIVE)
Definition: globals.cpp:228
bool IsCurrentlyRunningScriptTrusted()
Security function.
static wxString s_ScriptErrors
void OnScriptMenu(wxCommandEvent &event)
DLLIMPORT wxString cbC2U(const char *str)
Return str as a proper unicode-compatible string.
Definition: globals.cpp:733
wxFrame * GetAppFrame() const
Definition: manager.cpp:419
wxString LoadBufferRedirectOutput(const wxString &buffer)
Loads a string buffer and captures its output.
wxString GetErrorString(SquirrelError *exception=nullptr, bool clearErrors=true)
Returns an accumulated error string.
void SetHelp(const wxString &helpString)
LogManager * GetLogManager() const
Definition: manager.cpp:439
wxString Read(const wxString &key, const wxString &defaultVal=wxEmptyString)
bool IsOpened() const
void DisplayErrors(SquirrelError *exception=nullptr, bool clearErrors=true)
Display error dialog.
static wxString capture
const wxStringCharType * wx_str() const
virtual int ShowModal()
virtual void Clear()
Clear all managed menu items.
unsigned int wxUint32
wxString wxEmptyString
void InjectScriptOutput(const wxString &output)
Injects script output.
static void ScriptsPrintFunc(HSQUIRRELVM, const SQChar *s,...)
Definition: manager.h:183
cbEditor * Open(const wxString &filename, int pos=0, ProjectFile *data=nullptr)
const wxString & _(const wxString &string)
SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
#define cbThrow(message)
Definition: cbexception.h:42
wxArray< int > wxArrayInt
void PrintSquirrelToWxString(wxString &msg, const SQChar *s, va_list &vl)
bool IsEmpty() const
void Clear()
wxString GetPath(int flags=wxPATH_GET_VOLUME, wxPathFormat format=wxPATH_NATIVE) const
static void CaptureScriptOutput(HSQUIRRELVM, const SQChar *s,...)
MenuIDToScript m_MenuIDToScript
~ScriptingManager() override
Provides scripting in Code::Blocks.
char SQChar
void Log(const wxString &msg, int i=app_log, Logger::level lv=Logger::info)
Definition: logmanager.h:140
void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc)
Definition: sqapi.cpp:1249
bool UnRegisterScriptMenu(const wxString &menuPath)
Script-bound function to unregister a script&#39;s menu item.
void RefreshTrusts()
Force refresh of script trusts.
wxString & insert(size_t nPos, const wxString &str)
void DebugLog(const wxString &msg, Logger::level lv=Logger::info)
Definition: logmanager.h:146
bool RegisterScriptPlugin(const wxString &name, const wxArrayInt &ids)
Registers a script plugin menu IDs with the callback function.
Script trusts container struct.
bool RemoveTrust(const wxString &script)
Remove a script trust.
DLLIMPORT wxUint32 FromFile(const wxString &filename)
Definition: crc32.cpp:55
TrustedScripts m_TrustedScripts
bool LoadBuffer(const wxString &buffer, const wxString &debugName=_T("CommandLine"))
Loads a string buffer.
bool wxGetKeyState(wxKeyCode key)
static wxString Format(const wxString &format,...)
DLLIMPORT int cbMessageBox(const wxString &message, const wxString &caption=wxEmptyString, int style=wxOK, wxWindow *parent=NULL, int x=-1, int y=-1)
wxMessageBox wrapper.
Definition: globals.cpp:1395
bool UnRegisterAllScriptMenus()
Unregister all scripts&#39; menu items.
std::map< wxString, wxString > StringToStringMap
Definition: configmanager.h:54