-#include <squirrel.h>\r
-#include <assert.h>\r
-#include <sqstdblob.h>\r
-#include "sqrdbg.h"\r
-#include "sqdbgserver.h"\r
-\r
-\r
-#ifndef _UNICODE\r
-#define scstrcpy strcpy\r
-#else\r
-#define scstrcpy wcscpy\r
-#endif\r
-struct XMLEscape{\r
- const SQChar c;\r
- const SQChar *esc;\r
-};\r
-\r
-#define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")\r
-#define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")\r
-\r
-XMLEscape g_escapes[]={\r
- {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{0, NULL}\r
-};\r
-\r
-const SQChar *IntToString(int n)\r
-{\r
- static SQChar temp[256];\r
- scsprintf(temp,_SC("%d"),n);\r
- return temp;\r
-}\r
-\r
-int debug_hook(HSQUIRRELVM v);\r
-int error_handler(HSQUIRRELVM v);\r
-\r
-int beginelement(HSQUIRRELVM v)\r
-{\r
- SQUserPointer up;\r
- const SQChar *name;\r
- sq_getuserpointer(v,-1,&up);\r
- SQDbgServer *self = (SQDbgServer*)up;\r
- sq_getuserpointer(v,-1,&up);\r
- sq_getstring(v,2,&name);\r
- self->BeginElement(name);\r
- return 0;\r
-}\r
-\r
-int endelement(HSQUIRRELVM v)\r
-{\r
- SQUserPointer up;\r
- const SQChar *name;\r
- sq_getuserpointer(v,-1,&up);\r
- SQDbgServer *self = (SQDbgServer*)up;\r
- sq_getuserpointer(v,-1,&up);\r
- sq_getstring(v,2,&name);\r
- self->EndElement(name);\r
- return 0;\r
-}\r
-\r
-int attribute(HSQUIRRELVM v)\r
-{\r
- SQUserPointer up;\r
- const SQChar *name,*value;\r
- sq_getuserpointer(v,-1,&up);\r
- SQDbgServer *self = (SQDbgServer*)up;\r
- sq_getuserpointer(v,-1,&up);\r
- sq_getstring(v,2,&name);\r
- sq_getstring(v,3,&value);\r
- self->Attribute(name,value);\r
- return 0;\r
-}\r
-\r
-const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)\r
-{\r
- \r
- SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));\r
- SQChar *dest=temp;\r
- while(*s!=_SC('\0')){\r
- int i=0;\r
- bool escaped=false;\r
- while(g_escapes[i].esc!=NULL){\r
- if(*s==g_escapes[i].c){\r
- scstrcpy(dest,g_escapes[i].esc);\r
- dest+=scstrlen(g_escapes[i].esc);\r
- escaped=true;\r
- break;\r
- }\r
- i++;\r
- }\r
- if(!escaped){*dest=*s;*dest++;}\r
- *s++;\r
- }\r
- *dest=_SC('\0');\r
- return temp;\r
-}\r
-\r
-SQDbgServer::SQDbgServer(HSQUIRRELVM v)\r
-{\r
- _ready = false;\r
- _nestedcalls = 0;\r
- _autoupdate = false;\r
- _v = v;\r
- _state = eDBG_Running;\r
- _accept = INVALID_SOCKET;\r
- _endpoint = INVALID_SOCKET;\r
- _maxrecursion = 10;\r
- sq_resetobject(&_debugroot);\r
-}\r
-\r
-SQDbgServer::~SQDbgServer()\r
-{\r
- sq_release(_v,&_debugroot);\r
- if(_accept != INVALID_SOCKET)\r
- sqdbg_closesocket(_accept);\r
- if(_endpoint != INVALID_SOCKET)\r
- sqdbg_closesocket(_endpoint);\r
-}\r
-\r
-bool SQDbgServer::Init()\r
-{\r
- //creates an environment table for the debugger\r
- \r
- sq_newtable(_v);\r
- sq_getstackobj(_v,-1,&_debugroot);\r
- sq_addref(_v,&_debugroot);\r
-\r
- //creates a emptyslot to store the watches\r
- sq_pushstring(_v,_SC("watches"),-1);\r
- sq_pushnull(_v);\r
- sq_createslot(_v,-3);\r
-\r
- sq_pushstring(_v,_SC("beginelement"),-1);\r
- sq_pushuserpointer(_v,this);\r
- sq_newclosure(_v,beginelement,1);\r
- sq_setparamscheck(_v,2,_SC(".s"));\r
- sq_createslot(_v,-3);\r
-\r
- sq_pushstring(_v,_SC("endelement"),-1);\r
- sq_pushuserpointer(_v,this);\r
- sq_newclosure(_v,endelement,1);\r
- sq_setparamscheck(_v,2,_SC(".s"));\r
- sq_createslot(_v,-3);\r
-\r
- sq_pushstring(_v,_SC("attribute"),-1);\r
- sq_pushuserpointer(_v,this);\r
- sq_newclosure(_v,attribute,1);\r
- sq_setparamscheck(_v,3,_SC(".ss"));\r
- sq_createslot(_v,-3);\r
-\r
- sq_pop(_v,1);\r
-\r
- //stores debug hook and error handler in the registry\r
- sq_pushregistrytable(_v);\r
-\r
- sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);\r
- sq_pushuserpointer(_v,this);\r
- sq_newclosure(_v,debug_hook,1);\r
- sq_createslot(_v,-3);\r
- \r
- sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);\r
- sq_pushuserpointer(_v,this);\r
- sq_newclosure(_v,error_handler,1);\r
- sq_createslot(_v,-3);\r
-\r
- \r
- sq_pop(_v,1);\r
-\r
- //sets the error handlers\r
- SetErrorHandlers();\r
- return true;\r
-}\r
-\r
-bool SQDbgServer::ReadMsg()\r
-{\r
- return false;\r
-}\r
-\r
-void SQDbgServer::BusyWait()\r
-{\r
- while( !ReadMsg() )\r
- sleep(0);\r
-}\r
-\r
-void SQDbgServer::SendChunk(const SQChar *chunk)\r
-{\r
- char *buf=NULL;\r
- int buf_len=0;\r
-#ifdef _UNICODE\r
- buf_len=(int)scstrlen(chunk)+1;\r
- buf=(char *)sq_getscratchpad(_v,(buf_len)*3);\r
- wcstombs((char *)buf,chunk,buf_len);\r
-#else\r
- buf_len=(int)scstrlen(chunk);\r
- buf=(char *)chunk;\r
-#endif\r
- send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);\r
-}\r
-\r
-\r
-void SQDbgServer::Terminated()\r
-{\r
- BeginElement(_SC("terminated"));\r
- EndElement(_SC("terminated"));\r
- ::usleep(200);\r
-}\r
-\r
-void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)\r
-{\r
- switch(_state){\r
- case eDBG_Running:\r
- if(type==_SC('l') && _breakpoints.size()) {\r
- BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));\r
- if(itr != _breakpoints.end()) {\r
- Break(line,src,_SC("breakpoint"));\r
- BreakExecution();\r
- }\r
- }\r
- break;\r
- case eDBG_Suspended:\r
- _nestedcalls=0;\r
- case eDBG_StepOver:\r
- switch(type){\r
- case _SC('l'):\r
- if(_nestedcalls==0) {\r
- Break(line,src,_SC("step"));\r
- BreakExecution();\r
- }\r
- break;\r
- case _SC('c'):\r
- _nestedcalls++;\r
- break;\r
- case _SC('r'):\r
- if(_nestedcalls==0){\r
- _nestedcalls=0;\r
- \r
- }else{\r
- _nestedcalls--;\r
- }\r
- break;\r
- }\r
- break;\r
- case eDBG_StepInto:\r
- switch(type){\r
- case _SC('l'):\r
- _nestedcalls=0;\r
- Break(line,src,_SC("step"));\r
- BreakExecution();\r
- break;\r
- \r
- }\r
- break;\r
- case eDBG_StepReturn:\r
- switch(type){\r
- case _SC('l'):\r
- break;\r
- case _SC('c'):\r
- _nestedcalls++;\r
- break;\r
- case _SC('r'):\r
- if(_nestedcalls==0){\r
- _nestedcalls=0;\r
- _state=eDBG_StepOver;\r
- }else{\r
- _nestedcalls--;\r
- }\r
- \r
- break;\r
- }\r
- break;\r
- case eDBG_Disabled:\r
- break;\r
- }\r
-}\r
-\r
-\r
-#define MSG_ID(x,y) ((y<<8)|x)\r
-//ab Add Breakpoint\r
-//rb Remove Breakpoint\r
-//sp Suspend\r
-void SQDbgServer::ParseMsg(const char *msg)\r
-{\r
- \r
- switch(*((unsigned short *)msg)){\r
- case MSG_ID('a','b'): {\r
- BreakPoint bp;\r
- if(ParseBreakpoint(msg+3,bp)){\r
- AddBreakpoint(bp);\r
- scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());\r
- }\r
- else\r
- scprintf(_SC("error parsing add breakpoint"));\r
- }\r
- break;\r
- case MSG_ID('r','b'): {\r
- BreakPoint bp;\r
- if(ParseBreakpoint(msg+3,bp)){\r
- RemoveBreakpoint(bp);\r
- scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());\r
- }else\r
- scprintf(_SC("error parsing remove breakpoint"));\r
- }\r
- break;\r
- case MSG_ID('g','o'):\r
- if(_state!=eDBG_Running){\r
- _state=eDBG_Running;\r
- BeginDocument();\r
- BeginElement(_SC("resumed"));\r
- EndElement(_SC("resumed"));\r
- EndDocument();\r
-// Send(_SC("<resumed/>\r\n"));\r
- scprintf(_SC("go (execution resumed)\n"));\r
- }\r
- break;\r
- case MSG_ID('s','p'):\r
- if(_state!=eDBG_Suspended){\r
- _state=eDBG_Suspended;\r
- scprintf(_SC("suspend\n"));\r
- }\r
- break;\r
- case MSG_ID('s','o'):\r
- if(_state==eDBG_Suspended){\r
- _state=eDBG_StepOver;\r
- }\r
- break;\r
- case MSG_ID('s','i'):\r
- if(_state==eDBG_Suspended){\r
- _state=eDBG_StepInto;\r
- scprintf(_SC("step into\n"));\r
- }\r
- break;\r
- case MSG_ID('s','r'):\r
- if(_state==eDBG_Suspended){\r
- _state=eDBG_StepReturn;\r
- scprintf(_SC("step return\n"));\r
- }\r
- break;\r
- case MSG_ID('d','i'):\r
- if(_state!=eDBG_Disabled){\r
- _state=eDBG_Disabled;\r
- scprintf(_SC("disabled\n"));\r
- }\r
- break;\r
- case MSG_ID('a','w'): {\r
- Watch w;\r
- if(ParseWatch(msg+3,w))\r
- {\r
- AddWatch(w);\r
- scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());\r
- }\r
- else\r
- scprintf(_SC("error parsing add watch"));\r
- }\r
- break;\r
- case MSG_ID('r','w'): {\r
- int id;\r
- if(ParseRemoveWatch(msg+3,id))\r
- {\r
- RemoveWatch(id);\r
- scprintf(_SC("added watch %d\n"),id);\r
- }\r
- else\r
- scprintf(_SC("error parsing remove watch"));\r
- }\r
- break;\r
- case MSG_ID('t','r'):\r
- scprintf(_SC("terminate from user\n"));\r
- break;\r
- case MSG_ID('r','d'):\r
- scprintf(_SC("ready\n"));\r
- _ready=true;\r
- break;\r
- default:\r
- scprintf(_SC("unknown packet"));\r
-\r
- }\r
-}\r
-\r
-/*\r
- see copyright notice in sqrdbg.h\r
-*/\r
-bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)\r
-{\r
- static char stemp[MAX_BP_PATH];\r
- char *ep=NULL;\r
- out._line=strtoul(msg,&ep,16);\r
- if(ep==msg || (*ep)!=':')return false;\r
- \r
- char *dest=stemp;\r
- ep++;\r
- while((*ep)!='\n' && (*ep)!='\0')\r
- {\r
- *dest=*ep;\r
- *dest++;*ep++;\r
- }\r
- *dest='\0';\r
- *dest++;\r
- *dest='\0';\r
-#ifdef _UNICODE\r
- int len=(int)strlen(stemp);\r
- SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));\r
- size_t destlen=mbstowcs(p,stemp,len);\r
- p[destlen]=_SC('\0');\r
- out._src=p;\r
-#else\r
- out._src=stemp;\r
-#endif\r
- return true;\r
-}\r
-\r
-bool SQDbgServer::ParseWatch(const char *msg,Watch &out)\r
-{\r
- char *ep=NULL;\r
- out._id=strtoul(msg,&ep,16);\r
- if(ep==msg || (*ep)!=':')return false;\r
-\r
- //char *dest=out._src;\r
- ep++;\r
- while((*ep)!='\n' && (*ep)!='\0')\r
- {\r
- out._exp.append(1,*ep);\r
- *ep++;\r
- }\r
- return true;\r
-}\r
-\r
-bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)\r
-{\r
- char *ep=NULL;\r
- id=strtoul(msg,&ep,16);\r
- if(ep==msg)return false;\r
- return true;\r
-}\r
-\r
-\r
-void SQDbgServer::BreakExecution()\r
-{\r
- _state=eDBG_Suspended;\r
- while(_state==eDBG_Suspended){\r
- if(SQ_FAILED(sq_rdbg_update(this)))\r
- exit(0);\r
- usleep(10);\r
- }\r
-}\r
-\r
-//COMMANDS\r
-void SQDbgServer::AddBreakpoint(BreakPoint &bp)\r
-{\r
- _breakpoints.insert(bp);\r
- BeginDocument();\r
- BeginElement(_SC("addbreakpoint"));\r
- Attribute(_SC("line"),IntToString(bp._line));\r
- Attribute(_SC("src"),bp._src.c_str());\r
- EndElement(_SC("addbreakpoint"));\r
- EndDocument();\r
-}\r
-\r
-void SQDbgServer::AddWatch(Watch &w)\r
-{\r
- _watches.insert(w);\r
-}\r
-\r
-void SQDbgServer::RemoveWatch(int id)\r
-{\r
- WatchSetItor itor=_watches.find(Watch(id,_SC("")));\r
- if(itor==_watches.end()){\r
- BeginDocument();\r
- BeginElement(_SC("error"));\r
- Attribute(_SC("desc"),_SC("the watch does not exists"));\r
- EndElement(_SC("error"));\r
- EndDocument();\r
- }\r
- else{\r
- _watches.erase(itor);\r
- scprintf(_SC("removed watch %d\n"),id);\r
- }\r
-}\r
-\r
-void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)\r
-{\r
- BreakPointSetItor itor=_breakpoints.find(bp);\r
- if(itor==_breakpoints.end()){\r
- BeginDocument();\r
- BeginElement(_SC("break"));\r
- Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));\r
- EndElement(_SC("break"));\r
- EndDocument();\r
- }\r
- else{\r
- BeginDocument();\r
- BeginElement(_SC("removebreakpoint"));\r
- Attribute(_SC("line"),IntToString(bp._line));\r
- Attribute(_SC("src"),bp._src.c_str());\r
- EndElement(_SC("removebreakpoint"));\r
- EndDocument();\r
- _breakpoints.erase(itor);\r
- }\r
-}\r
-\r
-void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)\r
-{\r
- if(!error){\r
- BeginDocument();\r
- BeginElement(_SC("break"));\r
- Attribute(_SC("line"),IntToString(line));\r
- Attribute(_SC("src"),src);\r
- Attribute(_SC("type"),type);\r
- SerializeState();\r
- EndElement(_SC("break"));\r
- EndDocument();\r
- }else{\r
- BeginDocument();\r
- BeginElement(_SC("break"));\r
- Attribute(_SC("line"),IntToString(line));\r
- Attribute(_SC("src"),src);\r
- Attribute(_SC("type"),type);\r
- Attribute(_SC("error"),error);\r
- SerializeState();\r
- EndElement(_SC("break"));\r
- EndDocument();\r
- }\r
-}\r
-\r
-void SQDbgServer::SerializeState()\r
-{\r
- sq_pushnull(_v);\r
- sq_setdebughook(_v);\r
- sq_pushnull(_v);\r
- sq_seterrorhandler(_v);\r
- const SQChar *sz;\r
- sq_pushobject(_v,_serializefunc);\r
- sq_pushobject(_v,_debugroot);\r
- sq_pushstring(_v,_SC("watches"),-1);\r
- sq_newtable(_v);\r
- for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)\r
- {\r
- sq_pushinteger(_v,i->_id);\r
- sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());\r
- sq_createslot(_v,-3);\r
- }\r
- sq_rawset(_v,-3);\r
- if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue))){\r
- if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))\r
- SendChunk(sz);\r
- }\r
- sq_pop(_v,2);\r
- \r
- SetErrorHandlers();\r
-}\r
-\r
-\r
-void SQDbgServer::SetErrorHandlers()\r
-{\r
- sq_pushregistrytable(_v);\r
- sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);\r
- sq_rawget(_v,-2);\r
- sq_setdebughook(_v);\r
- sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);\r
- sq_rawget(_v,-2);\r
- sq_seterrorhandler(_v);\r
- sq_pop(_v,1);\r
-}\r
-\r
-void SQDbgServer::BeginElement(const SQChar *name)\r
-{\r
- _xmlcurrentement++;\r
- XMLElementState *self = &xmlstate[_xmlcurrentement];\r
- scstrcpy(self->name,name);\r
- self->haschildren = false;\r
- if(_xmlcurrentement > 0) {\r
- XMLElementState *parent = &xmlstate[_xmlcurrentement-1];\r
- if(!parent->haschildren) {\r
- SendChunk(_SC(">")); // closes the parent tag\r
- parent->haschildren = true;\r
- }\r
- }\r
- _scratchstring.resize(2+scstrlen(name));\r
- scsprintf(&_scratchstring[0],_SC("<%s"),name);\r
- SendChunk(&_scratchstring[0]);\r
-}\r
-\r
-void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)\r
-{\r
- XMLElementState *self = &xmlstate[_xmlcurrentement];\r
- assert(!self->haschildren); //cannot have attributes if already has children\r
- const SQChar *escval = escape_xml(value);\r
- _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));\r
- scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);\r
- SendChunk(&_scratchstring[0]);\r
-}\r
-\r
-void SQDbgServer::EndElement(const SQChar *name)\r
-{\r
- XMLElementState *self = &xmlstate[_xmlcurrentement];\r
- assert(scstrcmp(self->name,name) == 0);\r
- if(self->haschildren) {\r
- _scratchstring.resize(4+scstrlen(name));\r
- scsprintf(&_scratchstring[0],_SC("</%s>"),name);\r
- SendChunk(&_scratchstring[0]);\r
- \r
- }\r
- else {\r
- SendChunk(_SC("/>"));\r
- }\r
- _xmlcurrentement--;\r
-}\r
-\r
-void SQDbgServer::EndDocument()\r
-{\r
- SendChunk(_SC("\r\n"));\r
-}\r
-\r
-//this can be done much better/faster(do we need that?)\r
-const SQChar *SQDbgServer::escape_xml(const SQChar *s)\r
-{\r
- SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));\r
- SQChar *dest=temp;\r
- while(*s!=_SC('\0')){\r
- int i=0;\r
- bool escaped=false;\r
- while(g_escapes[i].esc!=NULL){\r
- if(*s==g_escapes[i].c){\r
- scstrcpy(dest,g_escapes[i].esc);\r
- dest+=scstrlen(g_escapes[i].esc);\r
- escaped=true;\r
- break;\r
- }\r
- i++;\r
- }\r
- if(!escaped){*dest=*s;*dest++;}\r
- *s++;\r
- }\r
- *dest=_SC('\0');\r
- return temp;\r
- \r
-}\r
+#include <squirrel.h>
+#include <assert.h>
+#include <sqstdblob.h>
+#include "sqrdbg.h"
+#include "sqdbgserver.h"
+
+
+#ifndef _UNICODE
+#define scstrcpy strcpy
+#else
+#define scstrcpy wcscpy
+#endif
+struct XMLEscape{
+ const SQChar c;
+ const SQChar *esc;
+};
+
+#define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")
+#define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")
+
+XMLEscape g_escapes[]={
+ {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{0, NULL}
+};
+
+const SQChar *IntToString(int n)
+{
+ static SQChar temp[256];
+ scsprintf(temp,_SC("%d"),n);
+ return temp;
+}
+
+SQInteger debug_hook(HSQUIRRELVM v);
+SQInteger error_handler(HSQUIRRELVM v);
+
+SQInteger beginelement(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ self->BeginElement(name);
+ return 0;
+}
+
+SQInteger endelement(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ self->EndElement(name);
+ return 0;
+}
+
+SQInteger attribute(HSQUIRRELVM v)
+{
+ SQUserPointer up;
+ const SQChar *name,*value;
+ sq_getuserpointer(v,-1,&up);
+ SQDbgServer *self = (SQDbgServer*)up;
+ sq_getuserpointer(v,-1,&up);
+ sq_getstring(v,2,&name);
+ sq_getstring(v,3,&value);
+ self->Attribute(name,value);
+ return 0;
+}
+
+const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s)
+{
+
+ SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar));
+ SQChar *dest=temp;
+ while(*s!=_SC('\0')){
+ int i=0;
+ bool escaped=false;
+ while(g_escapes[i].esc!=NULL){
+ if(*s==g_escapes[i].c){
+ scstrcpy(dest,g_escapes[i].esc);
+ dest+=scstrlen(g_escapes[i].esc);
+ escaped=true;
+ break;
+ }
+ i++;
+ }
+ if(!escaped){*dest=*s;*dest++;}
+ *s++;
+ }
+ *dest=_SC('\0');
+ return temp;
+}
+
+SQDbgServer::SQDbgServer(HSQUIRRELVM v)
+{
+ _ready = false;
+ _nestedcalls = 0;
+ _autoupdate = false;
+ _v = v;
+ _state = eDBG_Running;
+ _accept = INVALID_SOCKET;
+ _endpoint = INVALID_SOCKET;
+ _maxrecursion = 10;
+ sq_resetobject(&_debugroot);
+}
+
+SQDbgServer::~SQDbgServer()
+{
+ sq_release(_v,&_debugroot);
+ if(_accept != INVALID_SOCKET)
+ sqdbg_closesocket(_accept);
+ if(_endpoint != INVALID_SOCKET)
+ sqdbg_closesocket(_endpoint);
+}
+
+bool SQDbgServer::Init()
+{
+ //creates an environment table for the debugger
+
+ sq_newtable(_v);
+ sq_getstackobj(_v,-1,&_debugroot);
+ sq_addref(_v,&_debugroot);
+
+ //creates a emptyslot to store the watches
+ sq_pushstring(_v,_SC("watches"),-1);
+ sq_pushnull(_v);
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("beginelement"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,beginelement,1);
+ sq_setparamscheck(_v,2,_SC(".s"));
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("endelement"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,endelement,1);
+ sq_setparamscheck(_v,2,_SC(".s"));
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,_SC("attribute"),-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,attribute,1);
+ sq_setparamscheck(_v,3,_SC(".ss"));
+ sq_createslot(_v,-3);
+
+ sq_pop(_v,1);
+
+ //stores debug hook and error handler in the registry
+ sq_pushregistrytable(_v);
+
+ sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,debug_hook,1);
+ sq_createslot(_v,-3);
+
+ sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+ sq_pushuserpointer(_v,this);
+ sq_newclosure(_v,error_handler,1);
+ sq_createslot(_v,-3);
+
+
+ sq_pop(_v,1);
+
+ //sets the error handlers
+ SetErrorHandlers();
+ return true;
+}
+
+bool SQDbgServer::ReadMsg()
+{
+ return false;
+}
+
+void SQDbgServer::BusyWait()
+{
+ while( !ReadMsg() )
+ sleep(0);
+}
+
+void SQDbgServer::SendChunk(const SQChar *chunk)
+{
+ char *buf=NULL;
+ int buf_len=0;
+#ifdef _UNICODE
+ buf_len=(int)scstrlen(chunk)+1;
+ buf=(char *)sq_getscratchpad(_v,(buf_len)*3);
+ wcstombs((char *)buf,chunk,buf_len);
+#else
+ buf_len=(int)scstrlen(chunk);
+ buf=(char *)chunk;
+#endif
+ send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);
+}
+
+
+void SQDbgServer::Terminated()
+{
+ BeginElement(_SC("terminated"));
+ EndElement(_SC("terminated"));
+ ::usleep(200);
+}
+
+void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func)
+{
+ switch(_state){
+ case eDBG_Running:
+ if(type==_SC('l') && _breakpoints.size()) {
+ BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));
+ if(itr != _breakpoints.end()) {
+ Break(line,src,_SC("breakpoint"));
+ BreakExecution();
+ }
+ }
+ break;
+ case eDBG_Suspended:
+ _nestedcalls=0;
+ case eDBG_StepOver:
+ switch(type){
+ case _SC('l'):
+ if(_nestedcalls==0) {
+ Break(line,src,_SC("step"));
+ BreakExecution();
+ }
+ break;
+ case _SC('c'):
+ _nestedcalls++;
+ break;
+ case _SC('r'):
+ if(_nestedcalls==0){
+ _nestedcalls=0;
+
+ }else{
+ _nestedcalls--;
+ }
+ break;
+ }
+ break;
+ case eDBG_StepInto:
+ switch(type){
+ case _SC('l'):
+ _nestedcalls=0;
+ Break(line,src,_SC("step"));
+ BreakExecution();
+ break;
+
+ }
+ break;
+ case eDBG_StepReturn:
+ switch(type){
+ case _SC('l'):
+ break;
+ case _SC('c'):
+ _nestedcalls++;
+ break;
+ case _SC('r'):
+ if(_nestedcalls==0){
+ _nestedcalls=0;
+ _state=eDBG_StepOver;
+ }else{
+ _nestedcalls--;
+ }
+
+ break;
+ }
+ break;
+ case eDBG_Disabled:
+ break;
+ }
+}
+
+
+#define MSG_ID(x,y) ((y<<8)|x)
+//ab Add Breakpoint
+//rb Remove Breakpoint
+//sp Suspend
+void SQDbgServer::ParseMsg(const char *msg)
+{
+
+ switch(*((unsigned short *)msg)){
+ case MSG_ID('a','b'): {
+ BreakPoint bp;
+ if(ParseBreakpoint(msg+3,bp)){
+ AddBreakpoint(bp);
+ scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());
+ }
+ else
+ scprintf(_SC("error parsing add breakpoint"));
+ }
+ break;
+ case MSG_ID('r','b'): {
+ BreakPoint bp;
+ if(ParseBreakpoint(msg+3,bp)){
+ RemoveBreakpoint(bp);
+ scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());
+ }else
+ scprintf(_SC("error parsing remove breakpoint"));
+ }
+ break;
+ case MSG_ID('g','o'):
+ if(_state!=eDBG_Running){
+ _state=eDBG_Running;
+ BeginDocument();
+ BeginElement(_SC("resumed"));
+ EndElement(_SC("resumed"));
+ EndDocument();
+// Send(_SC("<resumed/>\r\n"));
+ scprintf(_SC("go (execution resumed)\n"));
+ }
+ break;
+ case MSG_ID('s','p'):
+ if(_state!=eDBG_Suspended){
+ _state=eDBG_Suspended;
+ scprintf(_SC("suspend\n"));
+ }
+ break;
+ case MSG_ID('s','o'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepOver;
+ }
+ break;
+ case MSG_ID('s','i'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepInto;
+ scprintf(_SC("step into\n"));
+ }
+ break;
+ case MSG_ID('s','r'):
+ if(_state==eDBG_Suspended){
+ _state=eDBG_StepReturn;
+ scprintf(_SC("step return\n"));
+ }
+ break;
+ case MSG_ID('d','i'):
+ if(_state!=eDBG_Disabled){
+ _state=eDBG_Disabled;
+ scprintf(_SC("disabled\n"));
+ }
+ break;
+ case MSG_ID('a','w'): {
+ Watch w;
+ if(ParseWatch(msg+3,w))
+ {
+ AddWatch(w);
+ scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());
+ }
+ else
+ scprintf(_SC("error parsing add watch"));
+ }
+ break;
+ case MSG_ID('r','w'): {
+ int id;
+ if(ParseRemoveWatch(msg+3,id))
+ {
+ RemoveWatch(id);
+ scprintf(_SC("added watch %d\n"),id);
+ }
+ else
+ scprintf(_SC("error parsing remove watch"));
+ }
+ break;
+ case MSG_ID('t','r'):
+ scprintf(_SC("terminate from user\n"));
+ break;
+ case MSG_ID('r','d'):
+ scprintf(_SC("ready\n"));
+ _ready=true;
+ break;
+ default:
+ scprintf(_SC("unknown packet"));
+
+ }
+}
+
+/*
+ see copyright notice in sqrdbg.h
+*/
+bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)
+{
+ static char stemp[MAX_BP_PATH];
+ char *ep=NULL;
+ out._line=strtoul(msg,&ep,16);
+ if(ep==msg || (*ep)!=':')return false;
+
+ char *dest=stemp;
+ ep++;
+ while((*ep)!='\n' && (*ep)!='\0')
+ {
+ *dest=*ep;
+ *dest++;*ep++;
+ }
+ *dest='\0';
+ *dest++;
+ *dest='\0';
+#ifdef _UNICODE
+ int len=(int)strlen(stemp);
+ SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar));
+ size_t destlen=mbstowcs(p,stemp,len);
+ p[destlen]=_SC('\0');
+ out._src=p;
+#else
+ out._src=stemp;
+#endif
+ return true;
+}
+
+bool SQDbgServer::ParseWatch(const char *msg,Watch &out)
+{
+ char *ep=NULL;
+ out._id=strtoul(msg,&ep,16);
+ if(ep==msg || (*ep)!=':')return false;
+
+ //char *dest=out._src;
+ ep++;
+ while((*ep)!='\n' && (*ep)!='\0')
+ {
+ out._exp.append(1,*ep);
+ *ep++;
+ }
+ return true;
+}
+
+bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id)
+{
+ char *ep=NULL;
+ id=strtoul(msg,&ep,16);
+ if(ep==msg)return false;
+ return true;
+}
+
+
+void SQDbgServer::BreakExecution()
+{
+ _state=eDBG_Suspended;
+ while(_state==eDBG_Suspended){
+ if(SQ_FAILED(sq_rdbg_update(this)))
+ exit(0);
+ usleep(10);
+ }
+}
+
+//COMMANDS
+void SQDbgServer::AddBreakpoint(BreakPoint &bp)
+{
+ _breakpoints.insert(bp);
+ BeginDocument();
+ BeginElement(_SC("addbreakpoint"));
+ Attribute(_SC("line"),IntToString(bp._line));
+ Attribute(_SC("src"),bp._src.c_str());
+ EndElement(_SC("addbreakpoint"));
+ EndDocument();
+}
+
+void SQDbgServer::AddWatch(Watch &w)
+{
+ _watches.insert(w);
+}
+
+void SQDbgServer::RemoveWatch(int id)
+{
+ WatchSetItor itor=_watches.find(Watch(id,_SC("")));
+ if(itor==_watches.end()){
+ BeginDocument();
+ BeginElement(_SC("error"));
+ Attribute(_SC("desc"),_SC("the watch does not exists"));
+ EndElement(_SC("error"));
+ EndDocument();
+ }
+ else{
+ _watches.erase(itor);
+ scprintf(_SC("removed watch %d\n"),id);
+ }
+}
+
+void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)
+{
+ BreakPointSetItor itor=_breakpoints.find(bp);
+ if(itor==_breakpoints.end()){
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));
+ EndElement(_SC("break"));
+ EndDocument();
+ }
+ else{
+ BeginDocument();
+ BeginElement(_SC("removebreakpoint"));
+ Attribute(_SC("line"),IntToString(bp._line));
+ Attribute(_SC("src"),bp._src.c_str());
+ EndElement(_SC("removebreakpoint"));
+ EndDocument();
+ _breakpoints.erase(itor);
+ }
+}
+
+void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error)
+{
+ if(!error){
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("line"),IntToString(line));
+ Attribute(_SC("src"),src);
+ Attribute(_SC("type"),type);
+ SerializeState();
+ EndElement(_SC("break"));
+ EndDocument();
+ }else{
+ BeginDocument();
+ BeginElement(_SC("break"));
+ Attribute(_SC("line"),IntToString(line));
+ Attribute(_SC("src"),src);
+ Attribute(_SC("type"),type);
+ Attribute(_SC("error"),error);
+ SerializeState();
+ EndElement(_SC("break"));
+ EndDocument();
+ }
+}
+
+void SQDbgServer::SerializeState()
+{
+ sq_pushnull(_v);
+ sq_setdebughook(_v);
+ sq_pushnull(_v);
+ sq_seterrorhandler(_v);
+ const SQChar *sz;
+ sq_pushobject(_v,_serializefunc);
+ sq_pushobject(_v,_debugroot);
+ sq_pushstring(_v,_SC("watches"),-1);
+ sq_newtable(_v);
+ for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)
+ {
+ sq_pushinteger(_v,i->_id);
+ sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length());
+ sq_createslot(_v,-3);
+ }
+ sq_rawset(_v,-3);
+ if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue,SQTrue))){
+ if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz)))
+ SendChunk(sz);
+ }
+ sq_pop(_v,2);
+
+ SetErrorHandlers();
+}
+
+
+void SQDbgServer::SetErrorHandlers()
+{
+ sq_pushregistrytable(_v);
+ sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);
+ sq_rawget(_v,-2);
+ sq_setdebughook(_v);
+ sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);
+ sq_rawget(_v,-2);
+ sq_seterrorhandler(_v);
+ sq_pop(_v,1);
+}
+
+void SQDbgServer::BeginElement(const SQChar *name)
+{
+ _xmlcurrentement++;
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ scstrcpy(self->name,name);
+ self->haschildren = false;
+ if(_xmlcurrentement > 0) {
+ XMLElementState *parent = &xmlstate[_xmlcurrentement-1];
+ if(!parent->haschildren) {
+ SendChunk(_SC(">")); // closes the parent tag
+ parent->haschildren = true;
+ }
+ }
+ _scratchstring.resize(2+scstrlen(name));
+ scsprintf(&_scratchstring[0],_SC("<%s"),name);
+ SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)
+{
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ assert(!self->haschildren); //cannot have attributes if already has children
+ const SQChar *escval = escape_xml(value);
+ _scratchstring.resize(5+scstrlen(name)+scstrlen(escval));
+ scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);
+ SendChunk(&_scratchstring[0]);
+}
+
+void SQDbgServer::EndElement(const SQChar *name)
+{
+ XMLElementState *self = &xmlstate[_xmlcurrentement];
+ assert(scstrcmp(self->name,name) == 0);
+ if(self->haschildren) {
+ _scratchstring.resize(4+scstrlen(name));
+ scsprintf(&_scratchstring[0],_SC("</%s>"),name);
+ SendChunk(&_scratchstring[0]);
+
+ }
+ else {
+ SendChunk(_SC("/>"));
+ }
+ _xmlcurrentement--;
+}
+
+void SQDbgServer::EndDocument()
+{
+ SendChunk(_SC("\r\n"));
+}
+
+//this can be done much better/faster(do we need that?)
+const SQChar *SQDbgServer::escape_xml(const SQChar *s)
+{
+ SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar));
+ SQChar *dest=temp;
+ while(*s!=_SC('\0')){
+ int i=0;
+ bool escaped=false;
+ while(g_escapes[i].esc!=NULL){
+ if(*s==g_escapes[i].c){
+ scstrcpy(dest,g_escapes[i].esc);
+ dest+=scstrlen(g_escapes[i].esc);
+ escaped=true;
+ break;
+ }
+ i++;
+ }
+ if(!escaped){*dest=*s;*dest++;}
+ *s++;
+ }
+ *dest=_SC('\0');
+ return temp;
+
+}