A few quick hacks to get the Squirrel debugger sqdbg to compile on Linux
[supertux.git] / external / squirrel / sqdbg / sqdbgserver.cpp
1 #include <squirrel.h>\r
2 #include <assert.h>\r
3 #include <stdio.h>\r
4 #include <string.h>\r
5 #include <stdlib.h>\r
6 #include <sqstdblob.h>\r
7 #include "sqrdbg.h"\r
8 #include "sqdbgserver.h"\r
9 \r
10 #ifndef _WIN32\r
11 #  define Sleep sleep\r
12 #  include <sys/types.h>\r
13 #  include <sys/socket.h>\r
14 #endif\r
15 \r
16 #ifndef _UNICODE\r
17 #define scstrcpy strcpy\r
18 #else\r
19 #define scstrcpy wcscpy\r
20 #endif\r
21 struct XMLEscape{\r
22         const SQChar c;\r
23         const SQChar *esc;\r
24 };\r
25 \r
26 #define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_")\r
27 #define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_")\r
28 \r
29 XMLEscape g_escapes[]={\r
30         {_SC('<'),_SC("&lt;")},{'>',_SC("&gt;")},{_SC('&'),_SC("&amp;")},{_SC('\''),_SC("&apos;")},{_SC('\"'),_SC("&quot;")},{_SC('\n'),_SC("&quot;n")},{_SC('\r'),_SC("&quot;r")},{NULL,NULL}\r
31 };\r
32 \r
33 const SQChar *IntToString(SQInteger n)\r
34 {\r
35         static SQChar temp[256];\r
36         scsprintf(temp,_SC("%d"),n);\r
37         return temp;\r
38 }\r
39 \r
40 const SQChar *PtrToString(void *p)\r
41 {\r
42         static SQChar temp[256];\r
43         scsprintf(temp,_SC("%p"),p);\r
44         return temp;\r
45 }\r
46 \r
47 SQInteger debug_hook(HSQUIRRELVM v);\r
48 SQInteger error_handler(HSQUIRRELVM v);\r
49 \r
50 SQInteger beginelement(HSQUIRRELVM v)\r
51 {\r
52         SQUserPointer up;\r
53         const SQChar *name;\r
54         sq_getuserpointer(v,-1,&up);\r
55         SQDbgServer *self = (SQDbgServer*)up;\r
56         sq_getuserpointer(v,-1,&up);\r
57         sq_getstring(v,2,&name);\r
58         self->BeginElement(name);\r
59         return 0;\r
60 }\r
61 \r
62 SQInteger endelement(HSQUIRRELVM v)\r
63 {\r
64         SQUserPointer up;\r
65         const SQChar *name;\r
66         sq_getuserpointer(v,-1,&up);\r
67         SQDbgServer *self = (SQDbgServer*)up;\r
68         sq_getuserpointer(v,-1,&up);\r
69         sq_getstring(v,2,&name);\r
70         self->EndElement(name);\r
71         return 0;\r
72 }\r
73 \r
74 SQInteger attribute(HSQUIRRELVM v)\r
75 {\r
76         SQUserPointer up;\r
77         const SQChar *name,*value;\r
78         sq_getuserpointer(v,-1,&up);\r
79         SQDbgServer *self = (SQDbgServer*)up;\r
80         sq_getuserpointer(v,-1,&up);\r
81         sq_getstring(v,2,&name);\r
82         sq_getstring(v,3,&value);\r
83         self->Attribute(name,value);\r
84         return 0;\r
85 }\r
86 \r
87 SQDbgServer::SQDbgServer(HSQUIRRELVM v)\r
88 {\r
89         _ready = false;\r
90         //_nestedcalls = 0;\r
91         _autoupdate = false;\r
92         _v = v;\r
93         _state = eDBG_Running;\r
94         _accept = INVALID_SOCKET;\r
95         _endpoint = INVALID_SOCKET;\r
96         //_maxrecursion = 10;\r
97         sq_resetobject(&_debugroot);\r
98 }\r
99 \r
100 SQDbgServer::~SQDbgServer()\r
101 {\r
102         VMStateMap::iterator itr = _vmstate.begin();\r
103         while(itr != _vmstate.end()) {\r
104                 VMState *vs = itr->second;\r
105                 delete vs;\r
106                 ++itr;\r
107         }\r
108         _vmstate.clear();\r
109         sq_pushobject(_v,_debugroot);\r
110         sq_clear(_v,-1);\r
111         sq_release(_v,&_debugroot);\r
112         if(_accept != INVALID_SOCKET)\r
113                 sqdbg_closesocket(_accept);\r
114         if(_endpoint != INVALID_SOCKET)\r
115                 sqdbg_closesocket(_endpoint);\r
116 }\r
117 \r
118 bool SQDbgServer::Init()\r
119 {\r
120         //creates  an environment table for the debugger\r
121         \r
122         sq_newtable(_v);\r
123         sq_getstackobj(_v,-1,&_debugroot);\r
124         sq_addref(_v,&_debugroot);\r
125 \r
126         //creates a emptyslot to store the watches\r
127         sq_pushstring(_v,_SC("watches"),-1);\r
128         sq_pushnull(_v);\r
129         sq_newslot(_v,-3, SQFalse);\r
130 \r
131         sq_pushstring(_v,_SC("beginelement"),-1);\r
132         sq_pushuserpointer(_v,this);\r
133         sq_newclosure(_v,beginelement,1);\r
134         sq_setparamscheck(_v,2,_SC(".s"));\r
135         sq_newslot(_v,-3, SQFalse);\r
136 \r
137         sq_pushstring(_v,_SC("endelement"),-1);\r
138         sq_pushuserpointer(_v,this);\r
139         sq_newclosure(_v,endelement,1);\r
140         sq_setparamscheck(_v,2,_SC(".s"));\r
141         sq_newslot(_v,-3, SQFalse);\r
142 \r
143         sq_pushstring(_v,_SC("attribute"),-1);\r
144         sq_pushuserpointer(_v,this);\r
145         sq_newclosure(_v,attribute,1);\r
146         sq_setparamscheck(_v,3,_SC(".ss"));\r
147         sq_newslot(_v,-3, SQFalse);\r
148 \r
149         sq_pop(_v,1);\r
150 \r
151         //stores debug hook and error handler in the registry\r
152         sq_pushregistrytable(_v);\r
153 \r
154         sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1);\r
155         sq_pushuserpointer(_v,this);\r
156         sq_newclosure(_v,debug_hook,1);\r
157         sq_newslot(_v,-3, SQFalse);\r
158         \r
159         sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1);\r
160         sq_pushuserpointer(_v,this);\r
161         sq_newclosure(_v,error_handler,1);\r
162         sq_newslot(_v,-3, SQFalse);\r
163 \r
164         \r
165         sq_pop(_v,1);\r
166 \r
167         //sets the error handlers\r
168         SetErrorHandlers(_v);\r
169         return true;\r
170 }\r
171 \r
172 bool SQDbgServer::ReadMsg()\r
173 {\r
174         return false;\r
175 }\r
176 \r
177 void SQDbgServer::BusyWait()\r
178 {\r
179         while( !ReadMsg() )\r
180                 Sleep(0);\r
181 }\r
182 \r
183 \r
184 \r
185 void SQDbgServer::SendChunk(const SQChar *chunk)\r
186 {\r
187         char *buf=NULL;\r
188         int buf_len=0;\r
189 #ifdef _UNICODE\r
190         buf_len=(int)scstrlen(chunk)+1;\r
191         buf=(char *)sq_getscratchpad(_v,(buf_len)*3);\r
192         //wcstombs((char *)buf,chunk,buf_len*3);\r
193         WideCharToMultiByte(CP_UTF8,0,chunk,-1,buf,buf_len*3,NULL,NULL);\r
194 #else\r
195         buf_len=(int)scstrlen(chunk);\r
196         buf=(char *)chunk;\r
197 #endif\r
198         send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0);\r
199 }\r
200 \r
201 \r
202 void SQDbgServer::Terminated()\r
203 {\r
204         BeginElement(_SC("terminated"));\r
205         EndElement(_SC("terminated"));\r
206         ::Sleep(200);\r
207 }\r
208 \r
209 VMState *SQDbgServer::GetVMState(HSQUIRRELVM v)\r
210 {\r
211         VMState *ret = NULL;\r
212         VMStateMap::iterator itr = _vmstate.find(v);\r
213         if(itr == _vmstate.end()) {\r
214                 ret = new VMState();\r
215                 _vmstate.insert(VMStateMap::value_type(v,ret));\r
216         }\r
217         else {\r
218                 ret = itr->second;\r
219         }\r
220         return ret;\r
221 }\r
222 \r
223 void SQDbgServer::Hook(HSQUIRRELVM v,SQInteger type,SQInteger line,const SQChar *src,const SQChar *func)\r
224 {\r
225         \r
226         VMState *vs = GetVMState(v);\r
227         switch(_state){\r
228         case eDBG_Running:\r
229                 if(type==_SC('l') && _breakpoints.size()) {\r
230                         BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src));\r
231                         if(itr != _breakpoints.end()) {\r
232                                 Break(v,line,src,_SC("breakpoint"));\r
233                                 BreakExecution();\r
234                         }\r
235                 }\r
236                 break;\r
237         case eDBG_Suspended:\r
238                 vs->_nestedcalls=0;\r
239         case eDBG_StepOver:\r
240                 switch(type){\r
241                 case _SC('l'):\r
242                         if(vs->_nestedcalls==0) {\r
243                                 Break(v,line,src,_SC("step"));\r
244                                 BreakExecution();\r
245                         }\r
246                         break;\r
247                 case _SC('c'):\r
248                         vs->_nestedcalls++;\r
249                         break;\r
250                 case _SC('r'):\r
251                         if(vs->_nestedcalls==0){\r
252                                 vs->_nestedcalls=0;\r
253                                 \r
254                         }else{\r
255                                 vs->_nestedcalls--;\r
256                         }\r
257                         break;\r
258                 }\r
259                 break;\r
260         case eDBG_StepInto:\r
261                 switch(type){\r
262                 case _SC('l'):\r
263                         vs->_nestedcalls=0;\r
264                         Break(v,line,src,_SC("step"));\r
265                         BreakExecution();\r
266                         break;\r
267                 \r
268                 }\r
269                 break;\r
270         case eDBG_StepReturn:\r
271                 switch(type){\r
272                 case _SC('l'):\r
273                         break;\r
274                 case _SC('c'):\r
275                         vs->_nestedcalls++;\r
276                         break;\r
277                 case _SC('r'):\r
278                         if(vs->_nestedcalls==0){\r
279                                 vs->_nestedcalls=0;\r
280                                 _state=eDBG_StepOver;\r
281                         }else{\r
282                                 vs->_nestedcalls--;\r
283                         }\r
284                         \r
285                         break;\r
286                 }\r
287                 break;\r
288         case eDBG_Disabled:\r
289                 break;\r
290         }\r
291 }\r
292 \r
293 \r
294 #define MSG_ID(x,y) ((y<<8)|x)\r
295 //ab Add Breakpoint\r
296 //rb Remove Breakpoint\r
297 //sp Suspend\r
298 void SQDbgServer::ParseMsg(const char *msg)\r
299 {\r
300         \r
301         switch(*((unsigned short *)msg)){\r
302                 case MSG_ID('a','b'): {\r
303                         BreakPoint bp;\r
304                         if(ParseBreakpoint(msg+3,bp)){\r
305                                 AddBreakpoint(bp);\r
306                                 scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str());\r
307                         }\r
308                         else\r
309                                 scprintf(_SC("error parsing add breakpoint"));\r
310                                                          }\r
311                         break;\r
312                 case MSG_ID('r','b'): {\r
313                         BreakPoint bp;\r
314                         if(ParseBreakpoint(msg+3,bp)){\r
315                                 RemoveBreakpoint(bp);\r
316                                 scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str());\r
317                         }else\r
318                                 scprintf(_SC("error parsing remove breakpoint"));\r
319                                                         }\r
320                         break;\r
321                 case MSG_ID('g','o'):\r
322                         if(_state!=eDBG_Running){\r
323                                 _state=eDBG_Running;\r
324                                 BeginDocument();\r
325                                         BeginElement(_SC("resumed"));\r
326                                         EndElement(_SC("resumed"));\r
327                                 EndDocument();\r
328 //                              Send(_SC("<resumed/>\r\n"));\r
329                                 scprintf(_SC("go (execution resumed)\n"));\r
330                         }\r
331                         break;\r
332                 case MSG_ID('s','p'):\r
333                         if(_state!=eDBG_Suspended){\r
334                                 _state=eDBG_Suspended;\r
335                                 scprintf(_SC("suspend\n"));\r
336                         }\r
337                         break;\r
338                 case MSG_ID('s','o'):\r
339                         if(_state==eDBG_Suspended){\r
340                                 _state=eDBG_StepOver;\r
341                         }\r
342                         break;\r
343                 case MSG_ID('s','i'):\r
344                         if(_state==eDBG_Suspended){\r
345                                 _state=eDBG_StepInto;\r
346                                 scprintf(_SC("step into\n"));\r
347                         }\r
348                         break;\r
349                 case MSG_ID('s','r'):\r
350                         if(_state==eDBG_Suspended){\r
351                                 _state=eDBG_StepReturn;\r
352                                 scprintf(_SC("step return\n"));\r
353                         }\r
354                         break;\r
355                 case MSG_ID('d','i'):\r
356                         if(_state!=eDBG_Disabled){\r
357                                 _state=eDBG_Disabled;\r
358                                 scprintf(_SC("disabled\n"));\r
359                         }\r
360                         break;\r
361                 case MSG_ID('a','w'): {\r
362                         Watch w;\r
363                         if(ParseWatch(msg+3,w))\r
364                         {\r
365                                 AddWatch(w);\r
366                                 scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str());\r
367                                 /*if(_state == eDBG_Suspended) {\r
368                                         Break(_line,_src.c_str(),_break_type.c_str());\r
369                                 }*/\r
370                         }\r
371                         else\r
372                                 scprintf(_SC("error parsing add watch"));\r
373                                                                 }\r
374                         break;\r
375                 case MSG_ID('r','w'): {\r
376                         SQInteger id;\r
377                         if(ParseRemoveWatch(msg+3,id))\r
378                         {\r
379                                 RemoveWatch(id);\r
380                                 scprintf(_SC("added watch %d\n"),id);\r
381                         }\r
382                         else\r
383                                 scprintf(_SC("error parsing remove watch"));\r
384                                                                 }\r
385                         break;\r
386                 case MSG_ID('t','r'):\r
387                         scprintf(_SC("terminate from user\n"));\r
388                         break;\r
389                 case MSG_ID('r','d'):\r
390                         scprintf(_SC("ready\n"));\r
391                         _ready=true;\r
392                         break;\r
393                 default:\r
394                         scprintf(_SC("unknown packet"));\r
395 \r
396         }\r
397 }\r
398 \r
399 bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out)\r
400 {\r
401         static char stemp[MAX_BP_PATH];\r
402         static SQChar desttemp[MAX_BP_PATH];\r
403         char *ep=NULL;\r
404         out._line=strtoul(msg,&ep,16);\r
405         if(ep==msg || (*ep)!=':')return false;\r
406         \r
407         char *dest=stemp;\r
408         ep++;\r
409         while((*ep)!='\n' && (*ep)!='\0')\r
410         {\r
411                 *dest=tolower(*ep);\r
412                 *dest++;*ep++;\r
413         }\r
414         *dest='\0';\r
415         *dest++;\r
416         *dest='\0';\r
417 #ifdef _UNICODE\r
418         int len=(int)strlen(stemp);\r
419         SQChar *p = desttemp;\r
420         size_t destlen = mbstowcs(p,stemp,len);\r
421         p[destlen]=_SC('\0');\r
422         out._src=p;\r
423 #else\r
424         out._src=stemp;\r
425 #endif\r
426         return true;\r
427 }\r
428 \r
429 bool SQDbgServer::ParseWatch(const char *msg,Watch &out)\r
430 {\r
431         char *ep=NULL;\r
432         out._id=strtoul(msg,&ep,16);\r
433         if(ep==msg || (*ep)!=':')return false;\r
434 \r
435         //char *dest=out._src;\r
436         ep++;\r
437         while((*ep)!='\n' && (*ep)!='\0')\r
438         {\r
439                 out._exp.append(1,*ep);\r
440                 *ep++;\r
441         }\r
442         return true;\r
443 }\r
444 \r
445 bool SQDbgServer::ParseRemoveWatch(const char *msg,SQInteger &id)\r
446 {\r
447         char *ep=NULL;\r
448         id=strtoul(msg,&ep,16);\r
449         if(ep==msg)return false;\r
450         return true;\r
451 }\r
452 \r
453 \r
454 void SQDbgServer::BreakExecution()\r
455 {\r
456         _state=eDBG_Suspended;\r
457         while(_state==eDBG_Suspended){\r
458                 if(SQ_FAILED(sq_rdbg_update(this)))\r
459                         exit(0);\r
460                 Sleep(10);\r
461         }\r
462 }\r
463 \r
464 //COMMANDS\r
465 void SQDbgServer::AddBreakpoint(BreakPoint &bp)\r
466 {\r
467         _breakpoints.insert(bp);\r
468         BeginDocument();\r
469                 BeginElement(_SC("addbreakpoint"));\r
470                         Attribute(_SC("line"),IntToString(bp._line));\r
471                         Attribute(_SC("src"),bp._src.c_str());\r
472                 EndElement(_SC("addbreakpoint"));\r
473         EndDocument();\r
474 }\r
475 \r
476 void SQDbgServer::AddWatch(Watch &w)\r
477 {\r
478         _watches.insert(w);\r
479 }\r
480 \r
481 void SQDbgServer::RemoveWatch(SQInteger id)\r
482 {\r
483         WatchSetItor itor=_watches.find(Watch(id,_SC("")));\r
484         if(itor==_watches.end()){\r
485                 BeginDocument();\r
486                 BeginElement(_SC("error"));\r
487                         Attribute(_SC("desc"),_SC("the watch does not exists"));\r
488                 EndElement(_SC("error"));\r
489         EndDocument();\r
490         }\r
491         else{\r
492                 _watches.erase(itor);\r
493                 scprintf(_SC("removed watch %d\n"),id);\r
494         }\r
495 }\r
496 \r
497 void SQDbgServer::RemoveBreakpoint(BreakPoint &bp)\r
498 {\r
499         BreakPointSetItor itor=_breakpoints.find(bp);\r
500         if(itor==_breakpoints.end()){\r
501                 BeginDocument();\r
502                         BeginElement(_SC("break"));\r
503                                 Attribute(_SC("desc"),_SC("the breakpoint doesn't exists"));\r
504                         EndElement(_SC("break"));\r
505                 EndDocument();\r
506         }\r
507         else{\r
508                 BeginDocument();\r
509                         BeginElement(_SC("removebreakpoint"));\r
510                                 Attribute(_SC("line"),IntToString(bp._line));\r
511                                 Attribute(_SC("src"),bp._src.c_str());\r
512                         EndElement(_SC("removebreakpoint"));\r
513                 EndDocument();\r
514                 _breakpoints.erase(itor);\r
515         }\r
516 }\r
517 \r
518 void SQDbgServer::Break(HSQUIRRELVM v,SQInteger line,const SQChar *src,const SQChar *type,const SQChar *error)\r
519 {\r
520         _line = line;\r
521         _src = src;\r
522         _break_type = src;\r
523         if(!error){\r
524                 BeginDocument();\r
525                         BeginElement(_SC("break"));\r
526                                 Attribute(_SC("thread"),PtrToString(v));\r
527                                 Attribute(_SC("line"),IntToString(line));\r
528                                 Attribute(_SC("src"),src);\r
529                                 Attribute(_SC("type"),type);\r
530                                 SerializeState(v);\r
531                         EndElement(_SC("break"));\r
532                 EndDocument();\r
533         }else{\r
534                 BeginDocument();\r
535                         BeginElement(_SC("break"));\r
536                                 Attribute(_SC("thread"),PtrToString(v));\r
537                                 Attribute(_SC("line"),IntToString(line));\r
538                                 Attribute(_SC("src"),src);\r
539                                 Attribute(_SC("type"),type);\r
540                                 Attribute(_SC("error"),error);\r
541                                 SerializeState(v);\r
542                         EndElement(_SC("break"));\r
543                 EndDocument();\r
544         }\r
545 }\r
546 \r
547 void SQDbgServer::SerializeState(HSQUIRRELVM v)\r
548 {\r
549         sq_pushnull(v);\r
550         sq_setdebughook(v);\r
551         sq_pushnull(v);\r
552         sq_seterrorhandler(v);\r
553         sq_pushobject(v,_serializefunc);\r
554         sq_pushobject(v,_debugroot);\r
555         sq_pushstring(v,_SC("watches"),-1);\r
556         sq_newtable(v);\r
557         for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i)\r
558         {\r
559                 sq_pushinteger(v,i->_id);\r
560                 sq_pushstring(v,i->_exp.c_str(),(SQInteger)i->_exp.length());\r
561                 sq_createslot(v,-3);\r
562         }\r
563         sq_rawset(v,-3);\r
564         if(SQ_SUCCEEDED(sq_call(v,1,SQTrue,SQFalse))){\r
565                 //if(SQ_SUCCEEDED(sqstd_getblob(v,-1,(SQUserPointer*)&sz)))\r
566                         //SendChunk(sz);\r
567         }\r
568         sq_pop(v,2);\r
569         \r
570         SetErrorHandlers(v);\r
571 }\r
572 \r
573 \r
574 void SQDbgServer::SetErrorHandlers(HSQUIRRELVM v)\r
575 {\r
576         sq_pushregistrytable(v);\r
577         sq_pushstring(v,SQDBG_DEBUG_HOOK,-1);\r
578         sq_rawget(v,-2);\r
579         sq_setdebughook(v);\r
580         sq_pushstring(v,SQDBG_ERROR_HANDLER,-1);\r
581         sq_rawget(v,-2);\r
582         sq_seterrorhandler(v);\r
583         sq_pop(v,1);\r
584 }\r
585 \r
586 void SQDbgServer::BeginDocument()\r
587\r
588         _xmlcurrentement = -1; \r
589         SendChunk(_SC("<?xml version='1.0' encoding='utf-8'?>"));\r
590 }\r
591 \r
592 void SQDbgServer::BeginElement(const SQChar *name)\r
593 {\r
594         _xmlcurrentement++;\r
595         XMLElementState *self = &xmlstate[_xmlcurrentement];\r
596         scstrcpy(self->name,name);\r
597         self->haschildren = false;\r
598         if(_xmlcurrentement > 0) {\r
599                 XMLElementState *parent = &xmlstate[_xmlcurrentement-1];\r
600                 if(!parent->haschildren) {\r
601                         SendChunk(_SC(">")); // closes the parent tag\r
602                         parent->haschildren = true;\r
603                 }\r
604         }\r
605         _scratchstring.resize(2+scstrlen(name));\r
606         scsprintf(&_scratchstring[0],_SC("<%s"),name);\r
607         SendChunk(&_scratchstring[0]);\r
608 }\r
609 \r
610 void SQDbgServer::Attribute(const SQChar *name,const SQChar *value)\r
611 {\r
612         XMLElementState *self = &xmlstate[_xmlcurrentement];\r
613         assert(!self->haschildren); //cannot have attributes if already has children\r
614         const SQChar *escval = escape_xml(value);\r
615         _scratchstring.resize(10+scstrlen(name)+scstrlen(escval));\r
616         scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval);\r
617         SendChunk(&_scratchstring[0]);\r
618 }\r
619 \r
620 void SQDbgServer::EndElement(const SQChar *name)\r
621 {\r
622         XMLElementState *self = &xmlstate[_xmlcurrentement];\r
623         assert(scstrcmp(self->name,name) == 0);\r
624         if(self->haschildren) {\r
625                 _scratchstring.resize(10+scstrlen(name));\r
626                 scsprintf(&_scratchstring[0],_SC("</%s>"),name);\r
627                 SendChunk(&_scratchstring[0]);\r
628                 \r
629         }\r
630         else {\r
631                 SendChunk(_SC("/>"));\r
632         }\r
633         _xmlcurrentement--;\r
634 }\r
635 \r
636 void SQDbgServer::EndDocument()\r
637 {\r
638         SendChunk(_SC("\r\n"));\r
639 }\r
640 \r
641 //this can be done much better/faster(do we need that?)\r
642 const SQChar *SQDbgServer::escape_xml(const SQChar *s)\r
643 {\r
644         SQChar *temp=sq_getscratchpad(_v,((SQInteger)scstrlen(s)*6) + sizeof(SQChar));\r
645         SQChar *dest=temp;\r
646         while(*s!=_SC('\0')){\r
647                 \r
648                 const SQChar *escape = NULL;\r
649                 switch(*s) {\r
650                         case _SC('<'): escape = _SC("&lt;"); break;\r
651                         case _SC('>'): escape = _SC("&gt;"); break;\r
652                         case _SC('&'): escape = _SC("&amp;"); break;\r
653                         case _SC('\''): escape = _SC("&apos;"); break;\r
654                         case _SC('\"'): escape = _SC("&quot;"); break;\r
655                         case _SC('\n'): escape = _SC("\\n"); break;\r
656                         case _SC('\r'): escape = _SC("\\r"); break;\r
657                 }\r
658                 if(escape) {\r
659                         scstrcpy(dest,escape);\r
660                         dest += scstrlen(escape);\r
661                 }\r
662                 else {\r
663                         *dest=*s;*dest++;\r
664                 }\r
665                 *s++;\r
666         }\r
667         *dest=_SC('\0');\r
668         return temp;\r
669         \r
670 }\r