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