ide: tweaked code related to managing (or not) debug session when compiling. fixed...
[sdk] / ide / src / debugger / Debugger.ec
1 import "ide"
2 import "process"
3 import "debugFindCtx"
4 import "debugTools"
5
6 //#define GDB_DEBUG_CONSOLE
7
8 import "PathBox"
9
10 extern char * strrchr(char * s, char c);
11
12 #define uint _uint
13 #include <stdarg.h>
14 #include <unistd.h>
15
16 #ifdef __APPLE__
17 #define __unix__
18 #endif
19
20 #if defined(__unix__)
21 #include <sys/stat.h>
22 #include <sys/time.h> // Required on Apple...
23 #endif
24 #undef uint
25
26 //bool eString_PathInsidePath(char * to, char * path)
27 bool eString_PathInsideOf(char * path, char * of)
28 {
29    if(!path[0] || !of[0])
30       return false;  // What to do here? Ever used?
31    else
32    {
33       char ofPart[MAX_FILENAME], ofRest[MAX_LOCATION];
34       char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
35       strcpy(ofRest, of);
36       strcpy(pathRest, path);
37       for(; ofRest[0] && pathRest[0];)
38       {
39          SplitDirectory(ofRest, ofPart, ofRest);      
40          SplitDirectory(pathRest, pathPart, pathRest);
41          if(fstrcmp(pathPart, ofPart))
42             return false;
43       }
44       if(!ofRest[0] && !pathRest[0])  // paths are identical - should return false or true? (changed to false)
45          return false;
46       else if(!pathRest[0])           // not inside of, it's the other way around
47          return false;
48       return true;
49    }
50 }
51
52 public char * StripQuotes2(char * string, char * output)
53 {
54    int c;
55    int d = 0;
56    bool quoted = false, escaped = false;
57    char ch;
58    for(c = 0; ch = string[c]; c++)
59    {
60       if(quoted)
61       {
62          if(escaped || ch != '\"')
63          {
64             output[d++] = ch;
65             if(ch == '\\')
66                escaped ^= true;
67          }
68          else
69             quoted = false;
70       }
71       else if(ch == '\"')
72          quoted = true;
73       else
74          output[d++] = ch;
75    }
76    output[d] = '\0';
77    return output;
78 }
79
80 // String Escape Copy
81 static void strescpy(char * d, char * s)
82 {
83    int j, k;
84    j = k = 0;
85    while(s[j])
86    {
87       switch(s[j])
88       {
89          case '\n':
90             d[k] = '\\';
91             d[++k] = 'n';
92             break;
93          case '\t':
94             d[k] = '\\';
95             d[++k] = 't';
96             break;
97          case '\a':
98             d[k] = '\\';
99             d[++k] = 'a';
100             break;
101          case '\b':
102             d[k] = '\\';
103             d[++k] = 'b';
104             break;
105          case '\f':
106             d[k] = '\\';
107             d[++k] = 'f';
108             break;
109          case '\r':
110             d[k] = '\\';
111             d[++k] = 'r';
112             break;
113          case '\v':
114             d[k] = '\\';
115             d[++k] = 'v';
116             break;
117          case '\\':
118             d[k] = '\\';
119             d[++k] = '\\';
120             break;
121          case '\"':
122             d[k] = '\\';
123             d[++k] = '\"';
124             break;
125          default:
126             d[k] = s[j];
127       }
128       ++j;
129       ++k;
130    }
131    d[k] = s[j];
132 }
133
134 static char * CopyUnescapedSystemPath(char * p)
135 {
136    char * d = new char[strlen(p) + 1];
137    struscpy(d, p);
138 #if defined(__WIN32__)
139    ChangeCh(d, '/', '\\');
140 #endif
141    return d;
142 }
143
144 static char * CopyUnescapedUnixPath(char * p)
145 {
146    char * d = new char[strlen(p) + 1];
147    struscpy(d, p);
148 #if defined(__WIN32__)
149    ChangeCh(d, '\\', '/');
150 #endif
151    return d;
152 }
153
154 static char * CopyUnescapedString(char * s)
155 {
156    char * d = new char[strlen(s) + 1];
157    struscpy(d, s);
158    return d;
159 }
160
161 // String Unescape Copy
162
163 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
164 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
165
166 static void struscpy(char * d, char * s)
167 {
168    int j, k;
169    j = k = 0;
170    while(s[j])
171    {
172       switch(s[j])
173       {
174          case '\\':
175             switch(s[++j])
176             {
177                case 'n':
178                   d[k] = '\n';
179                   break;
180                case 't':
181                   d[k] = '\t';
182                   break;
183                case 'a':
184                   d[k] = '\a';
185                   break;
186                case 'b':
187                   d[k] = '\b';
188                   break;
189                case 'f':
190                   d[k] = '\f';
191                   break;
192                case 'r':
193                   d[k] = '\r';
194                   break;
195                case 'v':
196                   d[k] = '\v';
197                   break;
198                case '\\':
199                   d[k] = '\\';
200                   break;
201                case '\"':
202                   d[k] = '\"';
203                   break;
204                default:
205                   d[k] = '\\';
206                   d[++k] = s[j];
207             }
208             break;
209          default:
210             d[k] = s[j];
211       }
212       ++j;
213       ++k;
214    }
215    d[k] = s[j];
216 }
217
218 static char * StripBrackets(char * string)
219 {
220    int length = strlen(string);
221    if(length > 1 && *string == '[' && string[length - 1] == ']')
222    {
223       *string = '\0';
224       string[length - 1] = '\0';
225       return ++string;
226    }
227    else
228       return string;
229 }
230
231 static char * StripCurlies(char * string)
232 {
233    int length = strlen(string);
234    if(length > 1 && *string == '{' && string[length - 1] == '}')
235    {
236       *string = '\0';
237       string[length - 1] = '\0';
238       return ++string;
239    }
240    else
241       return string;
242 }
243
244 static int StringGetInt(char * string, int start)
245 {
246    char number[8];
247    int i, len = strlen(string);
248    number[0] = '\0';
249    for(i = start; i < len && i < start + 8; i++)
250    {
251       if(string[i] == '0' || string[i] == '1' || string[i] == '2' || string[i] == '3' || string[i] == '4' || string[i] == '5' || string[i] == '6' || string[i] == '7' || string[i] == '8' || string[i] == '9')
252          strncat(number, &string[i], 1);
253       else
254          break;
255    }
256    return atoi(number);
257 }
258
259 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
260 {
261    uint level = 0;
262    
263    bool quoted = false; //bool escaped = false;
264    char * start = string;
265    
266    for(; *string; string++)
267    {
268       if(!start)
269          start = string;
270       if(quoted)
271       {
272          if(*string == '\"')
273             quoted = false;
274       }
275       else if(*string == '\"')
276          quoted = true;
277       else if(*string == '{' || *string == '[' || *string == '(' || *string == '<')
278          level++;
279       else if(*string == '}' || *string == ']' || *string == ')' || *string == '>')
280          level--;
281       else if(*string == seperator && !level)
282       {
283          tokens.Add(start);
284          *string = '\0';
285          start = null;
286       }
287    }
288    if(start)
289    {
290       //tokens[count] = start;
291       //tokens[count++] = start;
292       tokens.Add(start);
293       *string = '\0';
294    }
295    return tokens.count;
296 }
297
298 static bool TokenizeListItem(char * string, DebugListItem item)
299 {
300    char * equal = strstr(string, "=");
301    if(equal)
302    {
303       item.name = string;
304       *equal = '\0';
305       equal++;
306       item.value = equal;
307       equal = null;
308       return true;
309    }
310    else
311       return false;
312 }
313
314 static void DebuggerProtocolUnknown(char * message, char * gdbOutput)
315 {
316 #ifdef _DEBUG_GDB_PROTOCOL
317    ide.outputView.debugBox.Logf("Debugger Protocol Error: %s (%s)\n", message, gdbOutput);
318 #endif
319 }
320
321 // define GdbGetLineSize = 1638400;
322 define GdbGetLineSize = 5638400;
323 #if defined(__unix__)
324 char progFifoPath[MAX_LOCATION];
325 char progFifoDir[MAX_LOCATION];
326 #endif
327
328 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
329 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
330 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
331 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor };
332 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
333
334 FileDialog debuggerFileDialog { type = selectDir };
335
336 static DualPipe gdbHandle;
337 static DebugEvaluationData eval { };
338
339 static int targetProcessId;
340
341 static bool gdbReady;
342
343 class Debugger
344 {
345    Semaphore serialSemaphore { };
346    bool waitingForPID;
347    bool targeted;
348    bool symbols;
349    bool modules;
350    //bool breakpointsInserted;
351    bool sentKill;
352    bool sentBreakInsert;
353    bool ignoreBreakpoints;
354    bool userBreakOnInternBreak;
355    bool signalOn;
356    //bool watchesInit;
357
358    int ideProcessId;
359    int gdbProcessId;
360
361    int activeFrameLevel;
362    int activeThread;
363    int hitThread;
364    int signalThread;
365    int frameCount;
366
367    char * targetDir;
368    char * targetFile;
369    
370    DebuggerState state;
371    DebuggerEvent event;
372    DebuggerAction breakType;
373    //DebuggerCommand lastCommand;    // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
374
375    GdbDataStop stopItem;
376    GdbDataBreakpoint bpItem;
377    Frame activeFrame;
378    
379    List<Breakpoint> sysBPs { };
380    Breakpoint bpRunToCursor;
381    //Breakpoint bpStep;
382    Breakpoint bpHit;
383
384    OldList stackFrames;
385
386    ProjectConfig prjConfig;
387
388    CodeEditor codeEditor;
389
390    GdbThread gdbThread { debugger = this };
391    Timer gdbTimer
392    {
393       delay = 0.0, userData = this;
394
395       bool DelayExpired()
396       {
397          bool monitor = false;
398          DebuggerEvent curEvent = event;
399          GdbDataStop stopItem = this.stopItem;
400          if(!gdbReady)
401             return false;
402    
403          event = none;
404          if(this.stopItem)
405             this.stopItem = null;
406          else
407          {
408             if(curEvent && curEvent != exit)
409             {
410 #ifdef _DEBUG
411                printf("No stop item\n");
412 #endif
413             }
414          }
415
416          switch(breakType)
417          {
418             case restart:
419                breakType = none;
420                Restart();
421                break;
422             case stop:
423                breakType = none;
424                Stop();
425                break;
426             case selectFrame:
427             {
428                breakType = none;
429                GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
430                for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
431                   if(activeFrame.level == activeFrameLevel)
432                      break;
433                break;
434             }
435             //case bpValidation:
436             //   breakType = none;
437             //   GdbCommand(false, "-break-info %d", bpItem.number);
438             //   break;
439          }
440          
441          if(curEvent == none)
442             return false;
443          
444          switch (curEvent)
445          {
446             case breakEvent:
447                activeThread = stopItem.threadid;
448                GdbCommand(false, "-thread-list-ids");
449                GdbGetStack();
450                break;
451             case hit:
452                {
453                   Breakpoint bp = null;
454                
455                   for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
456                   {
457                      bp = i;
458                      break;
459                   }
460                   if(!bp)
461                   {
462                      for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
463                      {
464                         bp = i;
465                         break;
466                      }
467                   }
468                   bpHit = bp;
469                   
470                   if(!(!userBreakOnInternBreak && 
471                         bp && (bp.type == internalMain || bp.type == internalWinMain || bp.type == internalModulesLoaded)))
472                      monitor = true;
473                   hitThread = stopItem.threadid;
474                }
475                break;
476             case signal:
477                signalThread = stopItem.threadid;
478             case stepEnd:
479             case functionEnd:
480                monitor = true;
481                break;
482             case exit:
483                HideDebuggerViews();
484                break;
485          }
486          
487          if(monitor)
488          {
489             activeThread = stopItem.threadid;
490             GdbCommand(false, "-thread-list-ids");
491             GdbGetStack();
492             if(activeFrameLevel > 0)
493                GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
494
495             WatchesCodeEditorLinkInit();
496             EvaluateWatches();
497          }
498          
499          switch(curEvent)
500          {
501             case signal:
502             {
503                char * s;
504                signalOn = true;
505                ide.outputView.debugBox.Logf("Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
506                ide.outputView.debugBox.Logf("    %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
507                delete s;
508             }
509             case stepEnd:
510             case functionEnd:
511             case breakEvent:
512                // Why was SelectFrame missing here?
513                SelectFrame(activeFrameLevel);
514                GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
515                ide.Activate();
516                ide.Update(null);
517                if(curEvent == signal)
518                   ide.outputView.Show();
519                if(curEvent == signal || curEvent == breakEvent)
520                {
521                   if(curEvent == breakEvent)
522                      ide.threadsView.Show();
523                   ide.callStackView.Show();
524                }
525                ide.ShowCodeEditor();
526                if(curEvent == breakEvent)
527                   ide.callStackView.Activate();
528                break;
529             case hit:
530                EventHit(stopItem);
531                break;
532          }
533          
534          if(curEvent == DebuggerEvent::breakEvent || curEvent == stepEnd || curEvent == functionEnd)
535             ignoreBreakpoints = false;
536          
537          if(stopItem)
538          {
539             stopItem.Free();
540             delete stopItem;
541          }
542          return false;
543       }
544    };
545
546 #ifdef GDB_DEBUG_CONSOLE
547    char lastGdbOutput[GdbGetLineSize];
548 #endif
549 #if defined(__unix__)
550    ProgramThread progThread { };
551 #endif
552
553    void CleanUp()
554    {
555       // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
556
557       stackFrames.Free(Frame::Free);
558
559       delete targetDir;
560       delete targetFile;
561
562       ClearBreakDisplay();
563
564       // Clear Stuff up
565       gdbProcessId = 0;
566
567       waitingForPID = false;
568       targeted = false;
569       symbols = false;
570       modules = false;
571       sentKill = false;
572       sentBreakInsert = false;
573       ignoreBreakpoints = false;
574       userBreakOnInternBreak = false;
575       signalOn = false;
576
577       activeFrameLevel = 0;
578       activeThread = 0;
579       hitThread = 0;
580       signalThread = 0;
581       frameCount = 0;
582
583       targetDir = null;
584       targetFile = null;
585       
586       state = none;
587       event = none;
588       breakType = none;
589
590       stopItem = null;
591       bpItem = null;
592       activeFrame = 0;
593       
594       bpRunToCursor = null;
595       bpHit = null;
596
597       prjConfig = null;
598       codeEditor = null;
599
600       /*GdbThread gdbThread
601       Timer gdbTimer*/
602    }
603    
604    Debugger()
605    {
606       ideProcessId = Process_GetCurrentProcessId();
607
608       sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
609 #if defined(__WIN32__)
610       sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
611 #endif
612       sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
613       
614    }
615
616    ~Debugger()
617    {
618       Stop();
619       CleanUp();
620    }
621
622    // PUBLIC MEMBERS
623
624    property bool isActive { get { return state == running || state == stopped; } }
625    property bool isPrepared  { get { return state == loaded || state == running || state == stopped; } }
626
627    void Resume()
628    {
629       GdbExecContinue(true);
630    }
631
632    void Break()
633    {
634       if(state == running)
635       {
636          if(targetProcessId)
637          {
638             ide.DebugUpdateMenus(running, true);
639             GdbDebugBreak(false);
640          }
641       }
642    }
643
644    void Stop()
645    {
646       switch(state)
647       {
648          case running:
649             if(targetProcessId)
650             {
651                breakType = stop;
652                GdbDebugBreak(false);
653             }
654             break;
655          case stopped:
656             GdbAbortExec();
657          case loaded:
658             GdbExit();
659             break;
660       }
661    }
662
663    property bool isInDebugMode
664    {
665       get
666       {
667          if(state == running || state == stopped)
668             return true;
669          return false;
670       }
671    }
672
673    void Restart()
674    {
675       switch(state)
676       {
677          case running:
678             if(targetProcessId)
679             {
680                breakType = restart;
681                GdbDebugBreak(false);
682             }
683             break;
684          case stopped:
685             GdbAbortExec();
686          case loaded:
687             GdbExecRun();
688             break;
689       }
690    }
691
692    bool GoToCodeLine(char * location)
693    {
694       CodeLocation codloc;
695       codloc = CodeLocation::ParseCodeLocation(location);
696       if(codloc)
697       {
698          CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal);
699          if(editor)
700          {
701             EditBox editBox = editor.editBox;
702             editBox.GoToLineNum(codloc.line - 1);
703             editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
704             return true;
705          }
706       }
707       return false;
708    }
709
710    bool GoToStackFrameLine(int stackLevel, bool askForLocation)
711    {
712       if(ide)
713       {
714          char filePath[MAX_LOCATION];
715          char sourceDir[MAX_LOCATION];
716          Frame frame;
717          CodeEditor editor = null;
718          if(stackLevel == -1)  // this (the two lines) is part of that fix that I would not put in for some time
719             return false;
720          for(frame = stackFrames.first; frame; frame = frame.next)
721             if(frame.level == stackLevel)
722                break;
723          if(frame)
724          {
725             ide.callStackView.Show();
726
727             if(!frame.absoluteFile && frame.file)
728                frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
729             if(!frame.absoluteFile && askForLocation && frame.file)
730             {
731                char * s;
732                char title[MAX_LOCATION];
733
734                sprintf(title, "Provide source file location for %s", (s = CopySystemPath(frame.file)));
735                delete s;
736                if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
737                {
738                   AddSourceDir(sourceDir);
739                   frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
740                }
741             }
742             if(frame.absoluteFile)
743                editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal);
744             ide.Update(null);
745             if(editor && frame.line)
746             {
747                EditBox editBox = editor.editBox;
748                editBox.GoToLineNum(frame.line - 1);
749                editBox.GoToPosition(editBox.line, frame.line - 1, 0);
750                return true;
751             }
752          }
753       }
754       return false;
755    }
756
757    void SelectThread(int thread)
758    {
759       if(state == stopped)
760       {
761          if(thread != activeThread)
762          {
763             activeFrameLevel = -1;
764             ide.callStackView.Clear();
765             GdbCommand(false, "-thread-select %d", thread);
766             GdbGetStack();
767             // Why was SelectFrame missing here?
768             SelectFrame(activeFrameLevel);
769             GoToStackFrameLine(activeFrameLevel, true);
770             WatchesCodeEditorLinkRelease();
771             WatchesCodeEditorLinkInit();
772             EvaluateWatches();
773             ide.Update(null);
774          }
775          ide.callStackView.Show();
776       }
777    }
778
779    void SelectFrame(int frame)
780    {
781       if(state == stopped)
782       {
783          if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
784          {
785             activeFrameLevel = frame;  // there is no active frame number in the gdb reply
786             GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
787             for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
788                if(activeFrame.level == activeFrameLevel)
789                   break;
790
791             WatchesCodeEditorLinkRelease();
792             WatchesCodeEditorLinkInit();
793             EvaluateWatches();
794             ide.Update(null);
795          }
796       }
797    }
798
799    void HandleExit(char * reason, char * code)
800    {
801       bool returnedExitCode = false;
802       char verboseExitCode[128];
803       
804       state = loaded;
805       targetProcessId = 0;
806
807       if(code)
808          sprintf(verboseExitCode, " with exit code %s", code);
809       else
810          verboseExitCode[0] = '\0';
811       
812       event = exit;
813
814       // ClearBreakDisplay();
815
816       if(ide.workspace)
817       {
818          for(wh : ide.workspace.watches)
819          {
820             if(wh.type) FreeType(wh.type);
821             wh.type = null;
822             delete wh.value;
823             ide.watchesView.UpdateWatch(wh);
824          }
825       }
826
827       ide.DebugUpdateMenus(loaded, false);
828
829 #if defined(__unix__)
830       progThread.terminate = true;
831       if(fifoFile)
832       {
833          fifoFile.CloseInput();
834          app.Unlock();
835          progThread.Wait();
836          app.Lock();
837          delete fifoFile;
838       }         
839 #endif
840
841       {
842          char program[MAX_LOCATION];
843          GetSystemPathBuffer(program, targetFile);
844          if(!reason)
845             ide.outputView.debugBox.Logf("The program %s has exited%s.\n", program, verboseExitCode);
846          else if(!strcmp(reason, "exited-normally"))
847             ide.outputView.debugBox.Logf("The program %s has exited normally%s.\n", program, verboseExitCode);
848          else if(!strcmp(reason, "exited"))
849             ide.outputView.debugBox.Logf("The program %s has exited%s.\n", program, verboseExitCode);
850          else if(!strcmp(reason, "exited-signalled"))
851             ide.outputView.debugBox.Logf("The program %s has exited with a signal%s.\n", program, verboseExitCode);
852          else
853             ide.outputView.debugBox.Logf("The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
854       }
855       ide.Update(null);
856    }
857       
858    void Start()
859    {
860       ide.outputView.debugBox.Clear();
861       switch(state)
862       {
863          case none:
864          case terminated:
865             if(!GdbInit())
866                break;
867          case loaded:
868             GdbExecRun();
869             break;
870       }
871    }
872
873    void StepInto()
874    {
875       switch(state)
876       {
877          case none:
878          case terminated:
879             if(!GdbInit()) 
880                break;
881          case loaded:
882             ide.outputView.ShowClearSelectTab(debug);
883             ide.outputView.debugBox.Logf("Starting debug mode\n");
884             userBreakOnInternBreak = true;
885             GdbExecRun();
886             break;
887          case stopped:
888             GdbExecStep();
889             break;
890       }
891    }
892
893    void StepOver(bool ignoreBkpts)
894    {
895       switch(state)
896       {
897          case none:
898          case terminated:
899             if(!GdbInit()) 
900                break;
901          case loaded:
902             ide.outputView.ShowClearSelectTab(debug);
903             ide.outputView.debugBox.Logf("Starting debug mode\n");
904             ignoreBreakpoints = ignoreBkpts;
905             userBreakOnInternBreak = true;
906             GdbExecRun();
907             break;
908          case stopped:
909             ignoreBreakpoints = ignoreBkpts;
910             if(ignoreBreakpoints)
911                GdbBreakpointsDelete(true);
912             GdbExecNext();
913             break;
914       }
915    }
916
917    void StepOut(bool ignoreBkpts)
918    {
919       if(state == stopped)
920       {
921          ignoreBreakpoints = ignoreBkpts;
922          if(ignoreBreakpoints)
923             GdbBreakpointsDelete(true);
924          GdbExecFinish();
925       }
926    }
927
928    void RunToCursor(char * absoluteFilePath, int lineNumber, bool ignoreBkpts)
929    {
930       char relativeFilePath[MAX_LOCATION];
931       DebuggerState oldState = state;
932       ignoreBreakpoints = ignoreBkpts;
933       if(!ide.projectView.GetRelativePath(absoluteFilePath, relativeFilePath))
934          strcpy(relativeFilePath, absoluteFilePath);
935       switch(state)
936       {
937          case none:
938          case terminated:
939             Start();
940          case stopped:
941          case loaded:
942             if(symbols)
943             {
944                if(state == loaded)
945                {
946                   ide.outputView.ShowClearSelectTab(debug);
947                   ide.outputView.debugBox.Logf("Starting debug mode\n");
948                }
949                RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber);
950                sentBreakInsert = true;
951                GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
952                bpRunToCursor.bp = bpItem;
953                bpItem = null;
954                bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
955                ValidateBreakpoint(bpRunToCursor);
956             }
957             break;
958       }
959       switch(oldState)
960       {
961          case loaded:
962             if(ignoreBreakpoints)
963                GdbBreakpointsDelete(false);
964             GdbExecRun();
965             break;
966          case stopped:
967             if(ignoreBreakpoints)
968                GdbBreakpointsDelete(false);
969             GdbExecContinue(true);
970             break;
971       }
972       
973    }
974
975    void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
976    {
977       if(activeFrameLevel == -1)
978       {
979          *error = false;
980          *lineCursor = 0;
981          *lineTopFrame = 0;
982       }
983       else
984       {
985          *error = signalOn && activeThread == signalThread;
986          *lineCursor = activeFrameLevel + 1;
987          *lineTopFrame = activeFrameLevel ? 1 : 0;
988       }
989    }
990
991    int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
992    {
993       char winFilePath[MAX_LOCATION];
994       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
995       int count = 0;
996       Iterator<Breakpoint> it { ide.workspace.breakpoints };
997       while(it.Next() && count < max)
998       {
999          Breakpoint bp = it.data;
1000          if(bp.type == user)
1001          {
1002             if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1003             {
1004                lines[count] = bp.line;
1005                enabled[count] = bp.enabled;
1006                count++;
1007             }
1008          }
1009       }
1010       if(activeFrameLevel == -1)
1011       {
1012          *error = false;
1013          *lineCursor = 0;
1014          *lineTopFrame = 0;
1015       }
1016       else
1017       {
1018          *error = signalOn && activeThread == signalThread;
1019          if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1020             *lineCursor = activeFrame.line;
1021          else
1022             *lineCursor = 0;
1023          if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1024             *lineTopFrame = 0;
1025          else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1026             *lineTopFrame = stopItem.frame.line;
1027          else
1028             *lineTopFrame = 0;
1029          
1030          if(*lineTopFrame == *lineCursor && *lineTopFrame)
1031             *lineTopFrame = 0;
1032       }
1033       return count;
1034    }
1035
1036    void ChangeWatch(DataRow row, char * expression)
1037    {
1038       Watch wh = (Watch)row.tag;
1039       if(wh)
1040       {
1041          delete wh.expression;
1042          if(expression)
1043             wh.expression = CopyString(expression);
1044          else
1045          {
1046             Iterator<Watch> it { ide.workspace.watches };
1047             if(it.Find(wh))
1048                ide.workspace.watches.Delete(it.pointer);
1049          }
1050       }
1051       else if(expression)
1052       {
1053          wh = Watch { };
1054          row.tag = (int)wh;
1055          ide.workspace.watches.Add(wh);
1056          wh.row = row;
1057          wh.expression = CopyString(expression);
1058       }
1059       ide.workspace.Save();
1060       //if(expression && state == stopped)
1061       if(expression)
1062          ResolveWatch(wh);
1063    }
1064
1065    void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1066    {
1067       char winFilePath[MAX_LOCATION];
1068       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1069
1070       Link bpLink, next;
1071       for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1072       {
1073          Breakpoint bp = (Breakpoint)bpLink.data;
1074          next = bpLink.next;
1075
1076          if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1077          {
1078             if(bp.line > lineNumber || (bp.line == lineNumber && start))
1079             {
1080                if(move < 0 && (bp.line < lineNumber - move))
1081                   ide.workspace.RemoveBreakpoint(bp);
1082                else
1083                {
1084                   bp.line += move;
1085                   ide.breakpointsView.UpdateBreakpoint(bp.row);
1086                   ide.workspace.Save();
1087                }
1088             }
1089          }
1090       }
1091       
1092       // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1093    }
1094
1095    bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1096    {
1097       bool result;
1098       bool retry;
1099       String srcDir = null;
1100
1101       debuggerFileDialog.text = title;
1102       debuggerFileDialog.currentDirectory = startDir;
1103       debuggerFileDialog.master = ide;
1104
1105       while(debuggerFileDialog.Modal())
1106       {
1107          strcpy(sourceDir, debuggerFileDialog.filePath);
1108          if(!fstrcmp(ide.workspace.projectDir, sourceDir) && 
1109                   MessageBox { type = yesNo, master = ide, 
1110                               contents = "This is the project directory.\nWould you like to try again?", 
1111                               text = "Invalid Source Directory" }.Modal() == no)
1112             return false;
1113          else
1114          {
1115             for(dir : ide.workspace.sourceDirs)
1116             {
1117                if(!fstrcmp(dir, sourceDir))
1118                {
1119                   srcDir = dir;
1120                   break;
1121                }
1122             }
1123             
1124             if(srcDir && 
1125                   MessageBox { type = yesNo, master = ide, 
1126                               contents = "This source directory is already specified.\nWould you like to try again?", 
1127                               text = "Invalid Source Directory" }.Modal() == no)
1128                return false;
1129             else
1130             {
1131                if(test)
1132                {
1133                   char file[MAX_LOCATION];
1134                   strcpy(file, sourceDir);
1135                   PathCat(file, test);
1136                   result = FileExists(file);
1137                   if(!result && 
1138                         MessageBox { type = yesNo, master = ide, 
1139                                     contents = "Unable to locate source file.\nWould you like to try again?", 
1140                                     text = "Invalid Source Directory" }.Modal() == no)
1141                         return false;
1142                }
1143                else
1144                   result = true;
1145                
1146                if(result)
1147                   return true;
1148             }
1149          }
1150       }
1151       return false;
1152    }
1153
1154    void AddSourceDir(char * sourceDir)
1155    {
1156       ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1157       ide.workspace.Save();
1158       
1159       if(targeted)
1160       {
1161          DebuggerState oldState = state;
1162          switch(state)
1163          {
1164             case running:
1165                if(targetProcessId)
1166                   GdbDebugBreak(true);
1167             case stopped:
1168             case loaded:
1169                GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1170                break;
1171          }
1172          if(oldState == running)
1173             GdbExecContinue(false);
1174       }
1175    }
1176
1177    void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1178    {
1179       char winFilePath[MAX_LOCATION];
1180       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1181       char absolutePath[MAX_LOCATION];
1182       char relativePath[MAX_LOCATION];
1183       char sourceDir[MAX_LOCATION];
1184       Breakpoint bp = null;
1185
1186       strcpy(absolutePath, absoluteFilePath);
1187       for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1188       {
1189          bp = i;
1190          break;
1191       }
1192       if(bp)
1193       {
1194          if(bp.enabled)
1195          {
1196             ide.workspace.RemoveBreakpoint(bp);
1197             bp = null;
1198          }
1199          else
1200             bp.enabled = true;
1201       }
1202       else
1203       {
1204          // FIXED: This is how it should have been... Source locations are only for files not in project
1205          // if(eString_PathInsideOf(absolutePath, ide.workspace.projectDir))
1206          //   MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1207          bool result = false;
1208          if(prj)
1209             result = prj.GetRelativePath(absolutePath, relativePath);
1210          else
1211             ide.projectView.GetRelativePath(absolutePath, relativePath);
1212          //if(ide.projectView.GetRelativePath(absolutePath, relativePath));
1213          //else
1214          if(!result)
1215          {
1216             char title[MAX_LOCATION];
1217             char directory[MAX_LOCATION];
1218             StripLastDirectory(absolutePath, directory);
1219             sprintf(title, "Provide source files location directory for %s", absolutePath);
1220             while(true)
1221             {
1222                String srcDir = null;
1223                for(dir : ide.workspace.sourceDirs)
1224                {
1225                   if(eString_PathInsideOf(absolutePath, dir))
1226                   {
1227                      MakePathRelative(absoluteFilePath, dir, relativePath);
1228                      srcDir = dir;
1229                      break;
1230                   }
1231                }
1232                if(srcDir)
1233                   break;
1234                
1235                if(SourceDirDialog(title, directory, null, sourceDir))
1236                {
1237                   if(eString_PathInsideOf(absolutePath, sourceDir))
1238                   {
1239                      AddSourceDir(sourceDir);
1240                      MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1241                      break;
1242                   }
1243                   else if(MessageBox { type = yesNo, master = ide, 
1244                                  contents = "You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?", 
1245                                  text = "Invalid Source Directory" }.Modal() == no)
1246                      return;
1247                }
1248                else if(MessageBox { type = yesNo, master = ide, 
1249                                  contents = "You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?", 
1250                                  text = "No Source Directory Provided" }.Modal() == no)
1251                   return;
1252             }
1253          }
1254          ide.workspace.bpCount++;
1255          bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1256          ide.workspace.breakpoints.Add(bp);
1257          bp.absoluteFilePath = CopyString(absolutePath);
1258          bp.relativeFilePath = CopyString(relativePath);
1259          ide.breakpointsView.AddBreakpoint(bp);
1260       }
1261
1262       if(bp && targeted)
1263       {
1264          DebuggerState oldState = state;
1265          switch(state)
1266          {
1267             case running:
1268                if(targetProcessId)
1269                   GdbDebugBreak(true);
1270             case stopped:
1271             case loaded:
1272                if(symbols)
1273                {
1274                   sentBreakInsert = true;
1275                   GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1276                   bp.bp = bpItem;
1277                   bpItem = null;
1278                   bp.inserted = (bp.bp && bp.bp.number != 0);
1279                   ValidateBreakpoint(bp);
1280                }
1281                break;
1282          }
1283          if(oldState == running)
1284             GdbExecContinue(false);
1285       }
1286
1287       ide.workspace.Save();
1288    }
1289
1290    void UpdateRemovedBreakpoint(Breakpoint bp)
1291    {
1292       if(targeted && bp.inserted)
1293       {
1294          DebuggerState oldState = state;
1295          switch(state)
1296          {
1297             case running:
1298                if(targetProcessId)
1299                   GdbDebugBreak(true);
1300             case stopped:
1301             case loaded:
1302                if(symbols)
1303                   GdbCommand(false, "-break-delete %d", bp.bp.number);
1304                break;
1305          }
1306          if(oldState == running)
1307             GdbExecContinue(false);
1308       }
1309    }
1310
1311    // PRIVATE MEMBERS
1312
1313    void ParseFrame(Frame frame, char * string)
1314    {
1315       int i, j, k;
1316       Array<char *> frameTokens { minAllocSize = 50 };
1317       Array<char *> argsTokens { minAllocSize = 50 };
1318       Array<char *> argumentTokens { minAllocSize = 50 };
1319       DebugListItem item { };
1320       Argument arg;
1321       
1322       TokenizeList(string, ',', frameTokens);
1323       for(i = 0; i < frameTokens.count; i++)
1324       {
1325          if(TokenizeListItem(frameTokens[i], item))
1326          {
1327             StripQuotes(item.value, item.value);
1328             if(!strcmp(item.name, "level"))
1329                frame.level = atoi(item.value);
1330             else if(!strcmp(item.name, "addr"))
1331                frame.addr = CopyString(item.value);
1332             else if(!strcmp(item.name, "func"))
1333                frame.func = CopyString(item.value);
1334             else if(!strcmp(item.name, "args"))
1335             {
1336                if(!strcmp(item.value, "[]"))
1337                   frame.argsCount = 0;
1338                else
1339                {
1340                   item.value = StripBrackets(item.value);
1341                   TokenizeList(item.value, ',', argsTokens);
1342                   for(j = 0; j < argsTokens.count; j++)
1343                   {
1344                      argsTokens[j] = StripCurlies(argsTokens[j]);
1345                      TokenizeList(argsTokens[j], ',', argumentTokens);
1346                      for(k = 0; k < argumentTokens.count; k++)
1347                      {
1348                         arg = Argument { };
1349                         frame.args.Add(arg);
1350                         if(TokenizeListItem(argumentTokens[k], item))
1351                         {
1352                            if(!strcmp(item.name, "name"))
1353                            {
1354                               StripQuotes(item.value, item.value);
1355                               arg.name = CopyString(item.value);
1356                            }
1357                            else if(!strcmp(item.name, "value"))
1358                            {
1359                               StripQuotes(item.value, item.value);
1360                               arg.value = CopyString(item.value);
1361                            }
1362                            else
1363                               DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1364                         }
1365                         else 
1366                            DebuggerProtocolUnknown("Bad frame args item", "");
1367                      }
1368                      argumentTokens.RemoveAll();
1369                   }
1370                   frame.argsCount = argsTokens.count;
1371                   argsTokens.RemoveAll();
1372                }
1373             }
1374             else if(!strcmp(item.name, "from"))
1375                frame.from = item.value;
1376             else if(!strcmp(item.name, "file"))
1377             {
1378                frame.file = item.value;
1379                frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1380             }
1381             else if(!strcmp(item.name, "line"))
1382                frame.line = atoi(item.value);
1383             else if(!strcmp(item.name, "fullname"))
1384             {
1385                // New GDB is giving us a full name... Any reason why we coudln't figure it out ourselves?
1386             }
1387             else
1388                DebuggerProtocolUnknown("Unknown frame member name", item.name);
1389          }
1390          else
1391             DebuggerProtocolUnknown("Bad frame", "");
1392       }
1393       
1394       delete frameTokens;
1395       delete argsTokens;
1396       delete argumentTokens;
1397       delete item;
1398    }
1399
1400    void ShowDebuggerViews()
1401    {
1402       ide.outputView.Show();
1403       ide.outputView.SelectTab(debug);
1404       ide.threadsView.Show();
1405       ide.callStackView.Show();
1406       ide.watchesView.Show();
1407       ide.Update(null);
1408    }
1409
1410    void HideDebuggerViews()
1411    {
1412       ide.RepositionWindows(true);
1413    }
1414
1415    void ::GdbCommand(bool focus, char * format, ...)
1416    {
1417       if(gdbHandle)
1418       {
1419          char string[MAX_F_STRING];
1420          
1421          va_list args;
1422          va_start(args, format);
1423          vsprintf(string, format, args);
1424          va_end(args);
1425          
1426          gdbReady = false;
1427          ide.debugger.serialSemaphore.TryWait();
1428
1429
1430 #ifdef GDB_DEBUG_CONSOLE
1431          Log(string); Log("\n");
1432 #endif
1433 #ifdef GDB_DEBUG_OUTPUT
1434          ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1435 #endif
1436 #ifdef GDB_DEBUG_GUI
1437          if(ide.gdbDialog)
1438             ide.gdbDialog.AddCommand(string);
1439 #endif
1440          gdbHandle.Printf("%s\n", string);
1441          
1442          if(focus)
1443             Process_ShowWindows(targetProcessId);
1444
1445          app.Unlock();
1446          ide.debugger.serialSemaphore.Wait();
1447          app.Lock();
1448       } 
1449    }
1450
1451    bool ValidateBreakpoint(Breakpoint bp)
1452    {
1453       if(modules)
1454       {
1455          if(bp.bp.line != bp.line)
1456          {
1457             if(!bp.bp.line)
1458             {
1459 #ifdef _DEBUG
1460                ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1461 #endif
1462                if(bp.inserted)
1463                {
1464                   //GdbCommand(false, "-break-delete %d", bp.bp.number);
1465                   //bp.inserted = false;
1466                }
1467                //bp.enabled = false;
1468                return false;
1469             }
1470             else
1471             {
1472                ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1473                bp.line = bp.bp.line;
1474             }
1475          }
1476       }
1477       return true;
1478    }
1479
1480    static void GdbInsertInternalBreakpoint()
1481    {
1482       if(symbols)
1483       {
1484          //if(!breakpointsInserted)
1485          {
1486             DirExpression objDir = ide.project.objDir;
1487             for(bp : sysBPs)
1488             {
1489                if(!bp.inserted)
1490                {
1491                   if(bp.type == internalMain)
1492                   {
1493                      sentBreakInsert = true;
1494                      GdbCommand(false, "-break-insert main");
1495                      bp.bp = bpItem;
1496                      bpItem = null;
1497                      bp.inserted = (bp.bp && bp.bp.number != 0);
1498                   }
1499 #if defined(__WIN32__)
1500                   else if(bp.type == internalWinMain)
1501                   {
1502                      sentBreakInsert = true;
1503                      GdbCommand(false, "-break-insert WinMain");
1504                      bp.bp = bpItem;
1505                      bpItem = null;
1506                      bp.inserted = (bp.bp && bp.bp.number != 0);
1507                   }
1508 #endif
1509                   else if(bp.type == internalModulesLoaded)
1510                   {
1511                      char path[MAX_LOCATION];
1512                      char name[MAX_LOCATION];
1513                      char fixedModuleName[MAX_FILENAME];
1514                      char line[16384];
1515                      int lineNumber;
1516                      bool moduleLoadBlock = false;
1517                      File f;
1518                      ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1519                      sprintf(name, "%s.main.ec", fixedModuleName);
1520                      strcpy(path, ide.workspace.projectDir);
1521                      PathCatSlash(path, objDir.dir);
1522                      PathCatSlash(path, name);
1523                      f = FileOpen(path, read);
1524                      if(f)
1525                      {
1526                         for(lineNumber = 1; !f.Eof(); lineNumber++)
1527                         {
1528                            if(f.GetLine(line, sizeof(line) - 1))
1529                            {
1530                               bool moduleLoadLine;
1531                               TrimLSpaces(line, line);
1532                               moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1533                               if(!moduleLoadBlock && moduleLoadLine)
1534                                  moduleLoadBlock = true;
1535                               else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1536                                  break;
1537                            }
1538                         }
1539                         if(!f.Eof())
1540                         {
1541                            char relative[MAX_LOCATION];
1542                            bp.absoluteFilePath = CopyString(path);
1543                            MakePathRelative(path, ide.workspace.projectDir, relative);
1544                            delete bp.relativeFilePath;
1545                            bp.relativeFilePath = CopyString(relative);
1546                            bp.line = lineNumber;
1547                            sentBreakInsert = true;
1548                            GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1549                            bp.bp = bpItem;
1550                            bpItem = null;
1551                            bp.inserted = (bp.bp && bp.bp.number != 0);
1552                            ValidateBreakpoint(bp);
1553                         }
1554                         delete f;
1555                      }
1556                      break;
1557                   }
1558                }
1559             }
1560             delete objDir;
1561          }
1562       }
1563    }
1564
1565    void GdbBreakpointsInsert()
1566    {
1567       if(symbols)
1568       {
1569          //if(!breakpointsInserted)
1570          {
1571             //if(!ignoreBreakpoints)
1572                //breakpointsInserted = true;
1573             for(bp : ide.workspace.breakpoints)
1574             {
1575                if(!bp.inserted && bp.type == user)
1576                {
1577                   if(!ignoreBreakpoints && bp.enabled)
1578                   {
1579                      sentBreakInsert = true;
1580                      GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1581                      bp.bp = bpItem;
1582                      bpItem = null;
1583                      bp.inserted = (bp.bp && bp.bp.number != 0);
1584                      bp.hits = 0;
1585                      ValidateBreakpoint(bp);
1586                   }
1587                   else
1588                   {
1589 #ifdef _DEBUG
1590                      if(bp.bp)
1591                         printf("problem\n");
1592 #endif
1593                      bp.bp = GdbDataBreakpoint { };
1594                   }
1595                }
1596             }
1597             if(bpRunToCursor && !bpRunToCursor.inserted)
1598             {
1599                sentBreakInsert = true;
1600                GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1601                bpRunToCursor.bp = bpItem;
1602                bpItem = null;
1603                bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1604                ValidateBreakpoint(bpRunToCursor);
1605             }
1606          }
1607       }
1608    }
1609
1610    void GdbBreakpointsDelete(bool deleteRunToCursor)
1611    {
1612       //breakpointsInserted = false;
1613       if(symbols)
1614       {
1615          for(bp : ide.workspace.breakpoints)
1616          {
1617             if(bp.bp)
1618                GdbCommand(false, "-break-delete %d", bp.bp.number);
1619             bp.inserted = false;
1620             bp.bp = bpItem;
1621             //check here (reply form -break-delete, returns bpitem?)
1622             bpItem = null;
1623          }
1624          if(deleteRunToCursor && bpRunToCursor)
1625          {
1626             GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1627             bpRunToCursor.inserted = false;
1628             bpRunToCursor.bp = bpItem;
1629             //check here (reply form -break-delete, returns bpitem?)
1630             bpItem = null;
1631          }
1632       }
1633    }
1634
1635    void GdbGetStack()
1636    {
1637       activeFrame = null;
1638       stackFrames.Free(Frame::Free);
1639       GdbCommand(false, "-stack-info-depth");
1640       if(!frameCount)
1641          GdbCommand(false, "-stack-info-depth 192");
1642       if(frameCount && frameCount <= 192)
1643          GdbCommand(false, "-stack-list-frames 0 191");
1644       else
1645       {
1646          GdbCommand(false, "-stack-list-frames 0 95");
1647          GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1648       }
1649       GdbCommand(false, "");
1650    }
1651
1652    bool GdbTargetSet()
1653    {
1654       if(!targeted)
1655       {
1656          char escaped[MAX_LOCATION];
1657          strescpy(escaped, targetFile);
1658          GdbCommand(false, "file \"%s\"", escaped);  //GDB/MI Missing Implementation -symbol-file, -target-attach
1659
1660          if(!symbols)
1661             return true;
1662
1663          for(prj : ide.workspace.projects)
1664          {
1665             if(prj == ide.workspace.projects.firstIterator.data)
1666                continue;
1667
1668             //PrintLn("THIS: ", (String)prj.topNode.path);
1669             GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1670             //GdbCommand(false, ""); // why this empty GDB command
1671          }
1672
1673          for(dir : ide.workspace.sourceDirs)
1674          {
1675             GdbCommand(false, "-environment-directory \"%s\"", dir);
1676             //GdbCommand(false, ""); // why this empty GDB command
1677          }
1678          GdbInsertInternalBreakpoint();
1679          targeted = true;
1680       }
1681       return true;
1682    }
1683
1684    void GdbTargetRelease()
1685    {
1686       if(targeted)
1687       {
1688          GdbBreakpointsDelete(true);
1689          GdbCommand(false, "file");  //GDB/MI Missing Implementation -target-detach
1690          targeted = false;
1691          symbols = true;
1692       }
1693    }
1694
1695    void GdbDebugBreak(bool internal)
1696    {
1697       if(targetProcessId)
1698       {
1699          if(internal)
1700             breakType = DebuggerAction::internal;
1701
1702          if(ide)
1703          {
1704             ide.DebugUpdateMenus(running, true);
1705             ide.Update(null);
1706          }
1707          app.Unlock();
1708          if(Process_Break(targetProcessId))  //GdbCommand(false, "-exec-interrupt");
1709             serialSemaphore.Wait();
1710          else
1711          {
1712             state = loaded;
1713             targetProcessId = 0;
1714             ide.DebugUpdateMenus(loaded, false);
1715          }
1716          app.Lock();
1717       }
1718       else
1719          ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1720    }
1721
1722    void GdbExecRun()
1723    {
1724       GdbTargetSet();
1725       GdbExecCommon();
1726       ShowDebuggerViews();
1727       GdbCommand(true, "-exec-run");
1728    }
1729
1730    void GdbExecContinue(bool focus)
1731    {
1732       GdbExecCommon();
1733       GdbCommand(focus, "-exec-continue");
1734    }
1735
1736    void GdbExecNext()
1737    {
1738       GdbExecCommon();
1739       GdbCommand(true, "-exec-next");
1740    }
1741
1742    void GdbExecStep()
1743    {
1744       GdbExecCommon();
1745       GdbCommand(true, "-exec-step");
1746    }
1747
1748    void GdbExecFinish()
1749    {
1750       GdbExecCommon();
1751       GdbCommand(true, "-exec-finish");
1752    }
1753
1754    void GdbExecCommon()
1755    {
1756       ClearBreakDisplay();
1757       GdbBreakpointsInsert();
1758    }
1759
1760 #ifdef GDB_DEBUG_GUI
1761    void SendGDBCommand(char * command)
1762    {
1763       DebuggerState oldState = state;
1764       switch(state)
1765       {
1766          case running:
1767             if(targetProcessId)
1768                GdbDebugBreak(true);
1769          case stopped:
1770          case loaded:
1771             GdbCommand(false, command);
1772             break;
1773       }
1774       if(oldState == running)
1775          GdbExecContinue(false);
1776    }
1777 #endif
1778
1779    void ClearBreakDisplay()
1780    {
1781       activeThread = 0;
1782       activeFrameLevel = -1;
1783       hitThread = 0;
1784       bpHit = null;
1785       signalThread = 0;
1786       signalOn = false;
1787       frameCount = 0;
1788       if(stopItem)
1789          stopItem.Free();
1790       delete stopItem;
1791       event = none;
1792       activeFrame = null;
1793       stackFrames.Free(Frame::Free);
1794       WatchesCodeEditorLinkRelease();
1795       ide.callStackView.Clear();
1796       ide.threadsView.Clear();
1797       ide.Update(null);
1798    }
1799
1800    bool GdbAbortExec()
1801    {
1802       sentKill = true;
1803       GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1804       return true;
1805    }
1806
1807    bool GdbInit()
1808    {
1809       bool result = true;
1810       char oldDirectory[MAX_LOCATION];
1811       char tempPath[MAX_LOCATION];
1812       char command[MAX_LOCATION];
1813       char oldPath[MAX_LOCATION * 65];
1814       Project project = ide.project;
1815       DirExpression targetDirExp = project.targetDir;
1816
1817       GetEnvironment("PATH", oldPath, sizeof(oldPath));
1818
1819       prjConfig = project.config;
1820
1821       state = loaded;
1822       sentKill = false;
1823       sentBreakInsert = false;
1824       symbols = true;
1825       targeted = false;
1826       modules = false;
1827       //breakpointsInserted = false;
1828       
1829       ide.outputView.ShowClearSelectTab(debug);
1830       ide.outputView.debugBox.Logf("Starting debug mode\n");
1831
1832 #ifdef GDB_DEBUG_CONSOLE
1833       Log("Starting GDB"); Log("\n");
1834 #endif
1835 #ifdef GDB_DEBUG_OUTPUT
1836       ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1837 #endif
1838
1839       strcpy(tempPath, ide.workspace.projectDir);
1840       PathCatSlash(tempPath, targetDirExp.dir);
1841       delete targetDir;
1842       targetDir = CopyString(tempPath);
1843       project.CatTargetFileName(tempPath);
1844       delete targetFile;
1845       targetFile = CopyString(tempPath);
1846
1847       GetWorkingDir(oldDirectory, MAX_LOCATION);
1848       if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1849       {
1850          char temp[MAX_LOCATION];
1851          strcpy(temp, ide.workspace.projectDir);
1852          PathCatSlash(temp, ide.workspace.debugDir);
1853          ChangeWorkingDir(temp);
1854       }
1855       else
1856          ChangeWorkingDir(ide.workspace.projectDir);
1857       
1858       ide.SetPath(true);
1859
1860       sprintf(command, "gdb -n -silent --interpreter=mi2"); //-async //\"%s\"
1861       gdbTimer.Start();
1862       gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1863       if(!gdbHandle)
1864       {
1865          ide.outputView.debugBox.Logf("Debugger Fatal Error: Couldn't start GDB\n");
1866          result = false;
1867       }
1868       if(result)
1869       {
1870          incref gdbHandle;
1871          gdbThread.Create();
1872
1873          gdbProcessId = gdbHandle.GetProcessID();
1874          if(!gdbProcessId)
1875          {
1876             ide.outputView.debugBox.Logf("Debugger Fatal Error: Couldn't get GDB process ID\n");
1877             result = false;
1878          }
1879          if(result)
1880          {
1881             app.Unlock();
1882             serialSemaphore.Wait();
1883             app.Lock();
1884
1885             if(!GdbTargetSet())
1886             {
1887                //state = terminated;
1888                //ide.DebugUpdateMenus(loaded, false);
1889                result = false;
1890             }
1891
1892             if(result)
1893             {
1894 #if defined(__unix__)
1895                {
1896                   CreateTemporaryDir(progFifoDir, "ecereide");
1897                   strcpy(progFifoPath, progFifoDir);
1898                   PathCat(progFifoPath, "ideprogfifo");
1899                   if(!mkfifo(progFifoPath, 0600))
1900                   {
1901                      //fileCreated = true;
1902                   }
1903                   else
1904                   {
1905                      //app.Lock();
1906                      ide.outputView.debugBox.Logf("err: Unable to create FIFO %s\n", progFifoPath);
1907                      //app.Unlock();
1908                   }
1909                }
1910
1911                progThread.terminate = false;
1912                progThread.Create();
1913 #endif
1914       
1915 #if defined(__WIN32__)
1916                GdbCommand(false, "-gdb-set new-console on");
1917 #endif
1918          
1919                GdbCommand(false, "-gdb-set verbose off");
1920                //GdbCommand(false, "-gdb-set exec-done-display on");
1921                GdbCommand(false, "-gdb-set step-mode off");
1922                GdbCommand(false, "-gdb-set unwindonsignal on");
1923                //GdbCommand(false, "-gdb-set shell on");
1924                GdbCommand(false, "set print elements 992");
1925                GdbCommand(false, "-gdb-set backtrace limit 100000");
1926
1927 #if defined(__unix__)
1928                GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
1929 #endif
1930
1931                GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
1932             }
1933          }
1934       }
1935
1936       ChangeWorkingDir(oldDirectory);
1937       SetEnvironment("PATH", oldPath);
1938
1939       if(!result)
1940          GdbExit();
1941       delete targetDirExp;
1942       return result;
1943    }
1944
1945    void GdbExit()
1946    {
1947       if(gdbHandle && gdbProcessId)
1948       {
1949          GdbCommand(false, "-gdb-exit");
1950
1951          if(gdbThread)
1952          {
1953             app.Unlock();
1954             gdbThread.Wait();
1955             app.Lock();
1956          }
1957          if(gdbHandle)
1958          {
1959             gdbHandle.Wait();
1960             delete gdbHandle;
1961          }
1962       }
1963       gdbTimer.Stop();
1964       state = terminated;
1965       prjConfig = null;
1966
1967       if(ide.workspace)
1968          for(bp : ide.workspace.breakpoints)
1969             bp.inserted = false;
1970       for(bp : sysBPs)
1971          bp.inserted = false;
1972       if(bpRunToCursor)
1973          bpRunToCursor.inserted = false;
1974       
1975       ide.outputView.debugBox.Logf("Debugging stopped\n");
1976       ClearBreakDisplay();
1977       ide.DebugUpdateMenus(loaded, false);
1978       ide.Update(null);
1979
1980 #if defined(__unix__)
1981       if(FileExists(progFifoPath)) //fileCreated)
1982       {
1983          progThread.terminate = true;
1984          if(fifoFile)
1985          {
1986             fifoFile.CloseInput();
1987             app.Unlock();
1988             progThread.Wait();
1989             app.Lock();
1990             delete fifoFile;
1991          }         
1992          DeleteFile(progFifoPath);
1993          progFifoPath[0] = '\0';
1994          rmdir(progFifoDir);
1995       }
1996 #endif
1997    }
1998
1999    void WatchesCodeEditorLinkInit()
2000    {
2001       /*
2002       char tempPath[MAX_LOCATION];
2003       char path[MAX_LOCATION];
2004       
2005       //void MakeFilePathProjectRelative(char * path, char * relativePath)
2006       if(!ide.projectView.GetRelativePath(activeFrame.file, tempPath))
2007          strcpy(tempPath, activeFrame.file);
2008       
2009       strcpy(path, ide.workspace.projectDir);
2010       PathCat(path, tempPath);
2011       codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2012       if(!codeEditor)
2013       {
2014          for(srcDir : ide.workspace.sourceDirs)
2015          {
2016             strcpy(path, srcDir);
2017             PathCat(path, tempPath);
2018             codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2019             if(codeEditor) break;
2020          }
2021       }
2022       */
2023
2024       if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2025          activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2026       if(!activeFrame || !activeFrame.absoluteFile)
2027          codeEditor = null;
2028       else
2029          codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal);
2030       if(codeEditor)
2031       {
2032          codeEditor.inUseDebug = true;
2033          incref codeEditor;
2034       }
2035       //watchesInit = true;
2036    }
2037
2038    void WatchesCodeEditorLinkRelease()
2039    {
2040       //if(watchesInit)
2041       {
2042          if(codeEditor)
2043          {
2044             codeEditor.inUseDebug = false;
2045             if(!codeEditor.visible)
2046                codeEditor.Destroy(0);
2047             delete codeEditor;
2048          }
2049       }
2050    }
2051
2052    bool ResolveWatch(Watch wh)
2053    {
2054       bool result = false;
2055       
2056       wh.Reset();
2057
2058       /*delete wh.value;
2059       if(wh.type) 
2060       {
2061          FreeType(wh.type);
2062          wh.type = null;
2063       }*/
2064
2065       if(wh.expression)
2066       {
2067          char watchmsg[MAX_F_STRING];
2068          if(state == stopped && !codeEditor)
2069             wh.value = CopyString("No source file found for selected frame");
2070          //if(codeEditor && state == stopped || state != stopped)
2071          else
2072          {
2073             Module backupPrivateModule;
2074             Context backupContext;
2075             Class backupThisClass;
2076             Expression exp;
2077             parseError = false;
2078
2079             backupPrivateModule = GetPrivateModule();
2080             backupContext = GetCurrentContext();
2081             backupThisClass = GetThisClass();
2082             if(codeEditor)
2083             {
2084                SetPrivateModule(codeEditor.privateModule);
2085                SetCurrentContext(codeEditor.globalContext);
2086                SetTopContext(codeEditor.globalContext);
2087                SetGlobalContext(codeEditor.globalContext);
2088                SetGlobalData(&codeEditor.globalData);
2089             }
2090          
2091             exp = ParseExpressionString(wh.expression);
2092             
2093             if(exp && !parseError)
2094             {
2095                if(GetPrivateModule())
2096                {
2097                   if(codeEditor)
2098                      DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2099                   ProcessExpressionType(exp);
2100                }
2101                wh.type = exp.expType;
2102                if(wh.type)
2103                   wh.type.refCount++;
2104                DebugComputeExpression(exp);
2105
2106                /*if(exp.hasAddress)
2107                {
2108                   char temp[MAX_F_STRING];
2109                   sprintf(temp, "0x%x", exp.address);
2110                   wh.address = CopyString(temp);
2111                   // wh.address = CopyStringf("0x%x", exp.address);
2112                }*/
2113 /*
2114 //#ifdef _DEBUG
2115                {
2116                   Type dataType = exp.expType;
2117                   if(dataType)
2118                   {
2119                      char temp[MAX_F_STRING];
2120                      switch(dataType.kind)
2121                      {
2122                         case charType:
2123                            sprintf(temp, "%i", exp.val.c);
2124                            break;
2125                         case shortType:
2126                            sprintf(temp, "%i", exp.val.s);
2127                            break;
2128                         case intType:
2129                         case longType:
2130                         case enumType:
2131                            sprintf(temp, "%i", exp.val.i);
2132                            break;
2133                         case int64Type:
2134                            sprintf(temp, "%i", exp.val.i64);
2135                            break;
2136                         case pointerType:
2137                            sprintf(temp, "%i", exp.val.p);
2138                            break;
2139
2140                         case floatType:
2141                         {
2142                            long v = (long)exp.val.f;
2143                            sprintf(temp, "%i", v);
2144                            break;
2145                         } 
2146                         case doubleType:
2147                         {
2148                            long v = (long)exp.val.d;
2149                            sprintf(temp, "%i", v);
2150                            break;
2151                         }
2152                      }
2153                      if(temp)
2154                         wh.intVal = CopyString(temp);
2155                      switch(dataType.kind)
2156                      {
2157                         case charType:
2158                            sprintf(temp, "0x%x", exp.val.c);
2159                            break;
2160                         case shortType:
2161                            sprintf(temp, "0x%x", exp.val.s);
2162                            break;
2163                         case enumType:
2164                         case intType:
2165                            sprintf(temp, "0x%x", exp.val.i);
2166                            break;
2167                         case int64Type:
2168                            sprintf(temp, "0x%x", exp.val.i64);
2169                            break;
2170                         case longType:
2171                            sprintf(temp, "0x%x", exp.val.i64);
2172                            break;
2173                         case pointerType:
2174                            sprintf(temp, "0x%x", exp.val.p);
2175                            break;
2176
2177                         case floatType:
2178                         {
2179                            long v = (long)exp.val.f;
2180                            sprintf(temp, "0x%x", v);
2181                            break;
2182                         } 
2183                         case doubleType:
2184                         {
2185                            long v = (long)exp.val.d;
2186                            sprintf(temp, "0x%x", v);
2187                            break;
2188                         }
2189                      }
2190                      if(temp)
2191                         wh.hexVal = CopyString(temp);
2192                      switch(dataType.kind)
2193                      {
2194                         case charType:
2195                            sprintf(temp, "0o%o", exp.val.c);
2196                            break;
2197                         case shortType:
2198                            sprintf(temp, "0o%o", exp.val.s);
2199                            break;
2200                         case enumType:
2201                         case intType:
2202                            sprintf(temp, "0o%o", exp.val.i);
2203                            break;
2204                         case int64Type:
2205                            sprintf(temp, "0o%o", exp.val.i64);
2206                            break;
2207                         case longType:
2208                            sprintf(temp, "0o%o", exp.val.i64);
2209                            break;
2210                         case pointerType:
2211                            sprintf(temp, "0o%o", exp.val.p);
2212                            break;
2213
2214                         case floatType:
2215                         {
2216                            long v = (long)exp.val.f;
2217                            sprintf(temp, "0o%o", v);
2218                            break;
2219                         } 
2220                         case doubleType:
2221                         {
2222                            long v = (long)exp.val.d;
2223                            sprintf(temp, "0o%o", v);
2224                            break;
2225                         }
2226                      }
2227                      if(temp)
2228                         wh.octVal = CopyString(temp);
2229                   }
2230                }
2231                // WHATS THIS HERE ?
2232                if(exp.type == constantExp && exp.constant)
2233                   wh.constant = CopyString(exp.constant);
2234 //#endif
2235 */
2236
2237                switch(exp.type)
2238                {
2239                   case symbolErrorExp:
2240                      sprintf(watchmsg, "Symbol \"%s\" not found", exp.identifier.string);
2241                      break;
2242                   case structMemberSymbolErrorExp:
2243                      // todo get info as in next case (ExpClassMemberSymbolError)
2244                      sprintf(watchmsg, "Error: Struct member not found for \"%s\"", wh.expression);
2245                      break;
2246                   case classMemberSymbolErrorExp:
2247                      {
2248                         Class _class;
2249                         Expression memberExp = exp.member.exp;
2250                         Identifier memberID = exp.member.member;
2251                         Type type = memberExp.expType;
2252                         if(type)
2253                         {
2254                            _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2255                            if(!_class)
2256                            {
2257                               char string[256] = "";
2258                               Symbol classSym;
2259                               PrintType(type, string, false, true);
2260                               classSym = FindClass(string);
2261                               _class = classSym ? classSym.registered : null;
2262                            }
2263                            if(_class)
2264                               sprintf(watchmsg, "Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2265                            else
2266                               sprintf(watchmsg, "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2267                         }
2268                         else
2269                            sprintf(watchmsg, "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2270                      }
2271                      break;
2272                   case memoryErrorExp:
2273                      sprintf(watchmsg, "Memory can't be read at %s", (exp.type == constantExp) ? exp.constant : null);
2274                      break;
2275                   case dereferenceErrorExp:
2276                      sprintf(watchmsg, "Dereference failure for \"%s\"", wh.expression);
2277                      break;
2278                   case unknownErrorExp:
2279                      sprintf(watchmsg, "Unknown error for \"%s\"", wh.expression);
2280                      break;
2281                   case noDebuggerErrorExp:
2282                      sprintf(watchmsg, "Debugger required for symbol evaluation in \"%s\"", wh.expression);
2283                      break;
2284                   case debugStateErrorExp:
2285                      sprintf(watchmsg, "Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2286                      break;
2287                   case 0:
2288                      sprintf(watchmsg, "Null type for \"%s\"", wh.expression);
2289                      break;
2290                   case constantExp:
2291                   case stringExp:
2292                      // Temporary Code for displaying Strings
2293                      if((exp.expType && ((exp.expType.kind == pointerType || 
2294                               exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) || 
2295                            (wh.type && wh.type.kind == classType && wh.type._class && 
2296                               wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2297                               !strcmp(wh.type._class.registered.name, "String")))
2298                      {
2299
2300                         if(exp.expType.kind != arrayType || exp.hasAddress)
2301                         {
2302                            uint address;
2303                            char * string;
2304                            char value[4196];
2305                            int len;
2306                            //char temp[MAX_F_STRING * 32];
2307
2308                            ExpressionType evalError = dummyExp;
2309                            /*if(exp.expType.kind == arrayType)
2310                               sprintf(temp, "(char*)0x%x", exp.address);
2311                            else
2312                               sprintf(temp, "(char*)%s", exp.constant);*/
2313
2314                            //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2315                            address = strtoul(exp.constant, null, 0);
2316                            //printf("%x\n", address);
2317                            sprintf(value, "0x%08x ", address);
2318                            
2319                            if(!address)
2320                               strcat(value, "Null string");
2321                            else
2322                            {
2323                               int size = 4096;
2324                               len = strlen(value);
2325                               string = null;
2326                               while(!string && size > 2)
2327                               {
2328                                  string = GdbReadMemory(address, size);
2329                                  size /= 2;
2330                               }
2331                               if(string && string[0])
2332                               {
2333                                  value[len++] = '(';
2334                                  if(UTF8Validate(string))
2335                                  {
2336                                     int c;
2337                                     char ch;
2338                                     
2339                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2340                                        value[len++] = ch;                                 
2341                                     value[len++] = ')';
2342                                     value[len++] = '\0';
2343                                     
2344                                  }
2345                                  else
2346                                  {
2347                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2348                                     strcat(value, ") (ISO8859-1)");
2349                                  }
2350
2351                                  delete string;
2352                               }
2353                               else if(string)
2354                               {
2355                                  strcat(value, "Empty string");
2356                                  delete string;
2357                               }
2358                               else
2359                                  strcat(value, "Couldn't read memory");
2360                            }
2361                            wh.value = CopyString(value);
2362                         }
2363                      }
2364                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
2365                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
2366                      {
2367                         uint64 value = strtoul(exp.constant, null, 0);
2368                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2369                         EnumClassData enumeration = (EnumClassData)enumClass.data;
2370                         NamedLink item;
2371                         for(item = enumeration.values.first; item; item = item.next)
2372                            if((int)item.data == value)
2373                               break;
2374                         if(item)
2375                            wh.value = CopyString(item.name);
2376                         else
2377                            wh.value = CopyString("Invalid Enum Value");
2378                         result = (bool)atoi(exp.constant);
2379                      }
2380                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
2381                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2382                      {
2383                         unichar value;
2384                         int signedValue;
2385                         char charString[5];
2386                         char string[256];
2387
2388                         if(exp.constant[0] == '\'')
2389                         {
2390                            if((int)((byte *)exp.constant)[1] > 127)
2391                            {
2392                               int nb;
2393                               value = UTF8GetChar(exp.constant + 1, &nb);
2394                               if(nb < 2) value = exp.constant[1];
2395                               signedValue = value;
2396                            }
2397                            else
2398                            {
2399                               signedValue = exp.constant[1];
2400                               {
2401                                  // Precomp Syntax error with boot strap here:
2402                                  byte b = (byte)(char)signedValue;
2403                                  value = (unichar) b;
2404                               }
2405                            }
2406                         }
2407                         else
2408                         {
2409                            if(wh.type.kind == charType && wh.type.isSigned)
2410                            {
2411                               signedValue = (int)(char)strtol(exp.constant, null, 0);
2412                               {
2413                                  // Precomp Syntax error with boot strap here:
2414                                  byte b = (byte)(char)signedValue;
2415                                  value = (unichar) b;
2416                               }
2417                            }
2418                            else
2419                            {
2420                               value = strtoul(exp.constant, null, 0);
2421                               signedValue = (int)value;
2422                            }
2423                         }
2424                         charString[0] = 0;
2425                         UTF32toUTF8Len(&value, 1, charString, 5);
2426                         if(value == '\0')
2427                            sprintf(string, "\'\\0' (0)");
2428                         else if(value == '\t')
2429                            sprintf(string, "\'\\t' (%d)", value);
2430                         else if(value == '\n')
2431                            sprintf(string, "\'\\n' (%d)", value);
2432                         else if(value == '\r')
2433                            sprintf(string, "\'\\r' (%d)", value);
2434                         else if(wh.type.kind == charType && wh.type.isSigned)
2435                            sprintf(string, "\'%s\' (%d)", charString, signedValue);
2436                         else if(value > 256 || wh.type.kind != charType)
2437                         {
2438                            if(value > 0x10FFFF || !GetCharCategory(value))
2439                               sprintf(string, "Invalid Unicode Keypoint (0x%08X)", value);
2440                            else
2441                               sprintf(string, "\'%s\' (U+%04X)", charString, value);
2442                         }
2443                         else
2444                            sprintf(string, "\'%s\' (%d)", charString, value);
2445                         
2446                         wh.value = CopyString(string);
2447                         result = true;
2448                      }
2449                      else
2450                      {
2451                         wh.value = CopyString(exp.constant);
2452                         result = (bool)atoi(exp.constant);
2453                      }
2454                      break;
2455                   default:
2456                      if(exp.hasAddress)
2457                      {
2458                         wh.value = PrintHexUInt(exp.address);
2459                         result = (bool)exp.address;
2460                      }
2461                      else
2462                      {
2463                         char tempString[256];
2464                         sprintf(watchmsg, "Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
2465                               exp.type.OnGetString(tempString, null, null));
2466                      }
2467                      break;
2468                }
2469             }
2470             else
2471                sprintf(watchmsg, "Invalid expression: \"%s\"", wh.expression);
2472             if(exp) FreeExpression(exp);
2473
2474             
2475             SetPrivateModule(backupPrivateModule);
2476             SetCurrentContext(backupContext);
2477             SetTopContext(backupContext);
2478             SetGlobalContext(backupContext);
2479             SetThisClass(backupThisClass);
2480          }
2481          //else 
2482          //   wh.value = CopyString("No source file found for selected frame");
2483          
2484          if(!wh.value)
2485             wh.value = CopyString(watchmsg);
2486       }
2487       ide.watchesView.UpdateWatch(wh);
2488       return result;
2489    }
2490
2491    void EvaluateWatches()
2492    {
2493       for(wh : ide.workspace.watches)
2494          ResolveWatch(wh);
2495    }
2496
2497    char * ::GdbEvaluateExpression(char * expression)
2498    {
2499       eval.active = true;
2500       eval.error = none;
2501       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2502       if(eval.active)
2503          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2504       return eval.result;
2505    }
2506
2507    // to be removed... use GdbReadMemory that returns a byte array instead
2508    char * ::GdbReadMemoryString(uint address, int size, char format, int rows, int cols)
2509    {
2510       eval.active = true;
2511       eval.error = none;
2512       GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2513       if(eval.active)
2514          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2515       return eval.result;
2516    }
2517
2518    byte * ::GdbReadMemory(uint address, int bytes)
2519    {
2520       eval.active = true;
2521       eval.error = none;
2522       GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2523       if(eval.active)
2524          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2525       else if(eval.result && strcmp(eval.result, "N/A"))
2526       {
2527          byte * result = new byte[bytes];
2528          byte * string = eval.result;
2529          int c = 0;
2530          while(true)
2531          {
2532             result[c++] = (byte)strtol(string, &string, 10);
2533             if(string)
2534             {
2535                if(*string == ',')
2536                   string++;
2537                 else
2538                   break;
2539             }
2540             else
2541                break;
2542          }
2543          return result;
2544       }
2545       return null;
2546    }
2547
2548    void EventHit(GdbDataStop stopItem)
2549    {
2550       bool conditionMet = true;
2551       Breakpoint bp = bpHit;
2552       
2553       if(!bp && bpRunToCursor)
2554       {
2555          bp = bpRunToCursor;
2556          if(symbols)
2557             GdbCommand(false, "-break-delete %d", bp.bp.number);
2558       }
2559       
2560       if(bp)
2561       {
2562          if(bp.type == user && bp.line != stopItem.frame.line)
2563          {
2564             bp.line = stopItem.frame.line;
2565             ide.breakpointsView.UpdateBreakpoint(bp.row);
2566             ide.workspace.Save();
2567          }
2568
2569          switch(bp.type)
2570          {
2571             case internalMain:
2572             case internalWinMain:
2573                GdbBreakpointsInsert();
2574                if(userBreakOnInternBreak)
2575                {
2576                   userBreakOnInternBreak = false;
2577                   // Why was SelectFrame missing here?
2578                   SelectFrame(activeFrameLevel);
2579                   GoToStackFrameLine(activeFrameLevel, true);
2580                   ide.DebugUpdateMenus(stopped, false);
2581                   ide.Activate();
2582                   ide.Update(null);
2583                }
2584                else
2585                   GdbExecContinue(false);
2586                break;
2587             case internalModulesLoaded:
2588                modules = true;
2589                GdbBreakpointsInsert();
2590                GdbExecContinue(false);
2591                break;
2592             case user:
2593             case runToCursor:
2594                if(bp.condition)
2595                   conditionMet = ResolveWatch(bp.condition);
2596                if(conditionMet && (bp.level == -1 || bp.level == frameCount))
2597                {
2598                   bp.hits++;
2599                   if(bp.hits > bp.ignore)
2600                   {
2601                      ignoreBreakpoints = false;
2602                      // Why was SelectFrame missing here?
2603                      SelectFrame(activeFrameLevel);
2604                      GoToStackFrameLine(activeFrameLevel, true);
2605                      ide.DebugUpdateMenus(stopped, false);
2606                      ide.Activate();
2607                      ide.Update(null);
2608                      if(bp.type == BreakpointType::runToCursor)
2609                      {
2610                         delete bpRunToCursor;
2611                         bpRunToCursor = null;
2612                      }
2613                   }
2614                   else
2615                   {
2616                      /*bp.ignore--;
2617                      ide.breakpointsView.UpdateBreakpoint(bp.row);*/
2618                      GdbExecContinue(false);
2619                   }
2620                }
2621                else
2622                   GdbExecContinue(false);
2623                break;
2624          }
2625       }
2626       else
2627          ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2628    }
2629
2630    void GdbThreadExit()
2631    {
2632       if(state != terminated)
2633       {
2634          state = terminated;
2635          targetProcessId = 0;
2636          ClearBreakDisplay();
2637          ide.DebugUpdateMenus(loaded, false);
2638
2639          if(gdbHandle)
2640          {
2641             serialSemaphore.Release();
2642             gdbTimer.Stop();
2643             gdbHandle.Wait();
2644             delete gdbHandle;
2645             
2646             ide.outputView.debugBox.Logf("Debugger Fatal Error: GDB lost\n");
2647             ide.outputView.debugBox.Logf("Debugging stopped\n");
2648             ide.Update(null);
2649          }
2650          //state = terminated;
2651       }
2652    }
2653
2654    void GdbThreadMain(char * output)
2655    {
2656       int i;
2657       Array<char *> outTokens { minAllocSize = 50 };
2658       Array<char *> subTokens { minAllocSize = 50 };
2659       DebugListItem item { };
2660       DebugListItem item2 { };
2661       bool setWaitingForPID = false;
2662       
2663 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2664 #ifdef GDB_DEBUG_CONSOLE
2665       Log(output); Log("\n");
2666 #endif
2667 #ifdef GDB_DEBUG_OUTPUT
2668       {
2669          int len = strlen(output);
2670          if(len > 1024)
2671          {
2672             int c;
2673             char * start;
2674             char tmp[1025];
2675             tmp[1024] = '\0';
2676             start = output;
2677             for(c = 0; c < len / 1024; c++)
2678             {
2679                strncpy(tmp, start, 1024);
2680                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2681                start += 1024;
2682             }
2683             ide.outputView.gdbBox.Logf("out: %s\n", start);
2684          }
2685          else
2686          {
2687             ide.outputView.gdbBox.Logf("out: %s\n", output);
2688          }
2689       }
2690 #endif
2691 #ifdef GDB_DEBUG_CONSOLE
2692          strcpy(lastGdbOutput, output);
2693 #endif
2694 #ifdef GDB_DEBUG_GUI
2695          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2696 #endif
2697 #endif
2698       
2699       switch(output[0])
2700       {
2701          case '~':
2702             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2703             {
2704                symbols = false;
2705                ide.outputView.debugBox.Logf("Target doesn't contain debug information!\n");
2706                ide.Update(null);
2707             }
2708             break;
2709          case '^':
2710             gdbReady = false;
2711             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2712             {
2713                //if(outTokens.count == 1)
2714                {
2715                   if(sentKill)
2716                   {
2717                      sentKill = false;
2718                      state = loaded;
2719                      targetProcessId = 0;
2720                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2721                      {
2722                         if(!strcmp(item.name, "reason"))
2723                         {
2724                            char * reason = item.value;
2725                            StripQuotes(reason, reason);
2726                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2727                            {
2728                               char * exitCode;
2729                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2730                               {
2731                                  StripQuotes(item2.value, item2.value);
2732                                  if(!strcmp(item2.name, "exit-code"))
2733                                     exitCode = item2.value;
2734                                  else
2735                                     exitCode = null;
2736                               }
2737                               else
2738                                  exitCode = null;
2739                               HandleExit(reason, exitCode);
2740                            }
2741                         }
2742                         else
2743                            DebuggerProtocolUnknown("Unknown kill reply", item.name);
2744                      }
2745                      else
2746                         HandleExit(null, null);
2747                   }
2748                }
2749                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2750                {
2751                   if(!strcmp(item.name, "bkpt"))
2752                   {
2753                      sentBreakInsert = false;
2754 #ifdef _DEBUG
2755                      if(bpItem)
2756                         printf("problem\n");
2757 #endif
2758                      bpItem = GdbDataBreakpoint { };
2759                      item.value = StripCurlies(item.value);
2760                      TokenizeList(item.value, ',', subTokens);
2761                      for(i = 0; i < subTokens.count; i++)
2762                      {
2763                         if(TokenizeListItem(subTokens[i], item))
2764                         {
2765                            StripQuotes(item.value, item.value);
2766                            if(!strcmp(item.name, "number"))
2767                               bpItem.number = atoi(item.value);
2768                            else if(!strcmp(item.name, "type"))
2769                               bpItem.type = CopyString(item.value);
2770                            else if(!strcmp(item.name, "disp"))
2771                               bpItem.disp = CopyString(item.value);
2772                            else if(!strcmp(item.name, "enabled"))
2773                               bpItem.enabled = (!strcmpi(item.value, "y"));
2774                            else if(!strcmp(item.name, "addr"))
2775                               bpItem.addr = CopyString(item.value);
2776                            else if(!strcmp(item.name, "func"))
2777                               bpItem.func = CopyString(item.value);
2778                            else if(!strcmp(item.name, "file"))
2779                               bpItem.file = item.value;
2780                            else if(!strcmp(item.name, "line"))
2781                               bpItem.line = atoi(item.value);
2782                            else if(!strcmp(item.name, "at"))
2783                               bpItem.at = CopyString(item.value);
2784                            else if(!strcmp(item.name, "times"))
2785                               bpItem.times = atoi(item.value);
2786                         }
2787                      }
2788                      //breakType = bpValidation;
2789                      //app.SignalEvent();
2790                      subTokens.RemoveAll();
2791                   }
2792                   else if(!strcmp(item.name, "BreakpointTable"))
2793                      ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2794                   else if(!strcmp(item.name, "depth"))
2795                   {
2796                      StripQuotes(item.value, item.value);
2797                      frameCount = atoi(item.value);
2798                      activeFrame = null;
2799                      stackFrames.Free(Frame::Free);
2800                   }
2801                   else if(!strcmp(item.name, "stack"))
2802                   {
2803                      Frame frame;
2804                      if(stackFrames.count)
2805                         ide.callStackView.Logf("...\n");
2806                      else
2807                         activeFrame = null;
2808                      item.value = StripBrackets(item.value);
2809                      TokenizeList(item.value, ',', subTokens);
2810                      for(i = 0; i < subTokens.count; i++)
2811                      {
2812                         if(TokenizeListItem(subTokens[i], item))
2813                         {
2814                            if(!strcmp(item.name, "frame"))
2815                            {
2816                               frame = Frame { };
2817                               stackFrames.Add(frame);
2818                               item.value = StripCurlies(item.value);
2819                               ParseFrame(frame, item.value);
2820                               if(frame.file && frame.from)
2821                                  DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2822                               if(frame.file)
2823                               {
2824                                  char * s;
2825                                  if(activeFrameLevel == -1)
2826                                  {
2827                                     if(ide.projectView.IsModuleInProject(frame.file));
2828                                     {
2829                                        if(frame.level != 0)
2830                                        {
2831                                           //stopItem.frame = frame;
2832                                           breakType = selectFrame;
2833                                        }
2834                                        else
2835                                           activeFrame = frame;
2836                                        activeFrameLevel = frame.level;
2837                                     }
2838                                  }
2839                                  ide.callStackView.Logf("%3d ", frame.level);
2840                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2841                                     ide.callStackView.Logf("%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2842                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2843                                     ide.callStackView.Logf("%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2844                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2845                                     ide.callStackView.Logf("%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2846                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2847                                     ide.callStackView.Logf("%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2848                                  else
2849                                     ide.callStackView.Logf("%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2850                                  delete s;
2851                               }
2852                               else
2853                               {
2854                                  ide.callStackView.Logf("%3d ", frame.level);
2855
2856                                  if(frame.from)
2857                                  {
2858                                     char * s;
2859                                     ide.callStackView.Logf("inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2860                                     delete s;
2861                                  }
2862                                  else if(frame.func)
2863                                     ide.callStackView.Logf("%s\n", frame.func);
2864                                  else
2865                                     ide.callStackView.Logf("unknown source\n");
2866                               }
2867                            }
2868                            else
2869                               DebuggerProtocolUnknown("Unknown stack content", item.name);
2870                         }
2871                      }
2872                      if(activeFrameLevel == -1)
2873                      {
2874                         activeFrameLevel = 0;
2875                         activeFrame = stackFrames.first;
2876                      }
2877                      ide.callStackView.Home();
2878                      ide.Update(null);
2879                      subTokens.RemoveAll();
2880                   }
2881                   /*else if(!strcmp(item.name, "frame"))
2882                   {
2883                      Frame frame { };
2884                      item.value = StripCurlies(item.value);
2885                      ParseFrame(&frame, item.value);
2886                   }*/
2887                   else if(!strcmp(item.name, "thread-ids"))
2888                   {
2889                      ide.threadsView.Clear();
2890                      item.value = StripCurlies(item.value);
2891                      TokenizeList(item.value, ',', subTokens);
2892                      for(i = subTokens.count - 1; ; i--)
2893                      {
2894                         if(TokenizeListItem(subTokens[i], item))
2895                         {
2896                            if(!strcmp(item.name, "thread-id"))
2897                            {
2898                               int value;
2899                               StripQuotes(item.value, item.value);
2900                               value = atoi(item.value);
2901                               ide.threadsView.Logf("%3d \n", value);
2902                            }
2903                            else
2904                               DebuggerProtocolUnknown("Unknown threads content", item.name);
2905                         }
2906                         if(!i)
2907                            break;
2908                      }
2909                      ide.threadsView.Home();
2910                      ide.Update(null);
2911                      subTokens.RemoveAll();
2912                      //if(!strcmp(outTokens[2], "number-of-threads"))
2913                   }
2914                   else if(!strcmp(item.name, "new-thread-id"))
2915                   {
2916                      StripQuotes(item.value, item.value);
2917                      activeThread = atoi(item.value);
2918                   }
2919                   else if(!strcmp(item.name, "value"))
2920                   {
2921                      StripQuotes(item.value, item.value);
2922                      eval.result = CopyString(item.value);
2923                      eval.active = false;
2924                   }
2925                   else if(!strcmp(item.name, "addr"))
2926                   {
2927                      for(i = 2; i < outTokens.count; i++)
2928                      {
2929                         if(TokenizeListItem(outTokens[i], item))
2930                         {
2931                            if(!strcmp(item.name, "total-bytes"))
2932                            {
2933                               StripQuotes(item.value, item.value);
2934                               eval.bytes = atoi(item.value);
2935                            }
2936                            else if(!strcmp(item.name, "next-row"))
2937                            {
2938                               StripQuotes(item.value, item.value);
2939                               eval.nextBlockAddress = strtoul(item.value, null, 0);
2940                            }
2941                            else if(!strcmp(item.name, "memory"))
2942                            {
2943                               int j;
2944                               //int value;
2945                               //StripQuotes(item.value, item.value);
2946                               item.value = StripBrackets(item.value);
2947                               // this should be treated as a list...
2948                               item.value = StripCurlies(item.value);
2949                               TokenizeList(item.value, ',', subTokens);
2950                               for(j = 0; j < subTokens.count; j++)
2951                               {
2952                                  if(TokenizeListItem(subTokens[j], item))
2953                                  {
2954                                     if(!strcmp(item.name, "data"))
2955                                     {
2956                                        item.value = StripBrackets(item.value);
2957                                        StripQuotes2(item.value, item.value);
2958                                        eval.result = CopyString(item.value);
2959                                        eval.active = false;
2960                                     }
2961                                  }
2962                               }
2963                               subTokens.RemoveAll();
2964                            }
2965                         }
2966                      }
2967                   }
2968                   else if(!strcmp(item.name, "source-path"))
2969                   {
2970                   }
2971                   else
2972                      DebuggerProtocolUnknown("Unknown command reply", item.name);
2973                }
2974             }
2975             else if(!strcmp(outTokens[0], "^running"))
2976             {
2977                waitingForPID = true;
2978                setWaitingForPID = true;
2979             }
2980             else if(!strcmp(outTokens[0], "^exit"))
2981             {
2982                state = terminated;
2983                ide.DebugUpdateMenus(loaded, false);
2984                // ide.outputView.debugBox.Logf("Exit\n");
2985                ide.Update(null);
2986                gdbReady = true;
2987                serialSemaphore.Release();
2988             }
2989             else if(!strcmp(outTokens[0], "^error"))
2990             {
2991                if(sentBreakInsert)
2992                {
2993                   sentBreakInsert = false;
2994 #ifdef _DEBUG
2995                   if(bpItem)
2996                      printf("problem\n");
2997 #endif
2998                   bpItem = GdbDataBreakpoint { };
2999                }
3000
3001                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3002                {
3003                   if(!strcmp(item.name, "msg"))
3004                   {
3005                      StripQuotes(item.value, item.value);
3006                      if(eval.active)
3007                      {
3008                         eval.active = false;
3009                         eval.result = null;
3010                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3011                            eval.error = symbolNotFound;
3012                         else if(strstr(item.value, "Cannot access memory at address"))
3013                            eval.error = memoryCantBeRead;
3014                         else
3015                            eval.error = unknown;
3016                      }
3017                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3018                      {
3019                      }
3020                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3021                      {
3022                      }
3023                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3024                      {
3025                         state = stopped;
3026                         gdbHandle.Printf("-exec-continue\n");
3027                      }
3028                      else if(!strcmp(item.value, "ptrace: No such process."))
3029                      {
3030                         state = loaded;
3031                         targetProcessId = 0;
3032                         ide.DebugUpdateMenus(loaded, false);
3033                      }
3034                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3035                      {
3036                      }
3037                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3038                      {
3039                         state = loaded;
3040                         targetProcessId = 0;
3041                         ide.DebugUpdateMenus(loaded, false);
3042                      }
3043                      else if(strstr(item.value, "No such file or directory."))
3044                      {
3045                         state = loaded;
3046                         targetProcessId = 0;
3047                         ide.DebugUpdateMenus(loaded, false);
3048                      }
3049                      else
3050                      {
3051 #ifdef _DEBUG
3052                         if(strlen(item.value) < MAX_F_STRING)
3053                         {
3054                            char * s;
3055                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3056                            delete s;
3057                         }
3058                         else
3059                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3060 #endif
3061                      }
3062                   }
3063                }
3064                else
3065                   DebuggerProtocolUnknown("Unknown error content", item.name);
3066             }
3067             else
3068                DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3069             
3070             outTokens.RemoveAll();
3071             break;
3072          case '+':
3073             DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3074             break;
3075          case '=':
3076             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3077                ;
3078             else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3079                ;
3080             else if(!strcmp(outTokens[0], "=library-loaded")) //=library-loaded,id="/lib/ld-linux.so.2",target-name="/lib/ld-linux.so.2",host-name="/lib/ld-linux.so.2",symbols-loaded="0"
3081                ;
3082             else
3083                DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3084             outTokens.RemoveAll();
3085             break;
3086          case '*':
3087             gdbReady = false;
3088             if(TokenizeList(output, ',', outTokens))
3089             {
3090                if(!strcmp(outTokens[0],"*running"))
3091                {
3092                   waitingForPID = true;
3093                   setWaitingForPID = true;
3094                }
3095                else if(!strcmp(outTokens[0], "*stopped"))
3096                {
3097                   state = stopped;
3098                   
3099                   if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3100                   {
3101                      if(!strcmp(item.name, "reason"))
3102                      {
3103                         char * reason = item.value;
3104                         StripQuotes(reason, reason);
3105                         if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3106                         {
3107                            char * exitCode;
3108                            if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3109                            {
3110                               StripQuotes(item2.value, item2.value);
3111                               if(!strcmp(item2.name, "exit-code"))
3112                                  exitCode = item2.value;
3113                               else
3114                                  exitCode = null;
3115                            }
3116                            else
3117                               exitCode = null;
3118                            HandleExit(reason, exitCode);
3119                         }
3120                         else if(!strcmp(reason, "breakpoint-hit"))
3121                         {
3122    #ifdef _DEBUG
3123                            if(stopItem)
3124                               printf("problem\n");
3125    #endif
3126                            stopItem = GdbDataStop { };
3127
3128                            for(i = 2; i < outTokens.count; i++)
3129                            {
3130                               TokenizeListItem(outTokens[i], item);
3131                               StripQuotes(item.value, item.value);
3132                               if(!strcmp(item.name, "bkptno"))
3133                                  stopItem.bkptno = atoi(item.value);
3134                               else if(!strcmp(item.name, "thread-id"))
3135                                  stopItem.threadid = atoi(item.value);
3136                               else if(!strcmp(item.name, "frame"))
3137                               {
3138                                  item.value = StripCurlies(item.value);
3139                                  ParseFrame(stopItem.frame, item.value);
3140                               }
3141                               else
3142                                  DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3143                            }
3144
3145                            event = hit;
3146                         }
3147                         else if(!strcmp(reason, "end-stepping-range"))
3148                         {
3149    #ifdef _DEBUG
3150                            if(stopItem)
3151                               printf("problem\n");
3152    #endif
3153                            stopItem = GdbDataStop { };
3154
3155                            for(i = 2; i < outTokens.count; i++)
3156                            {
3157                               TokenizeListItem(outTokens[i], item);
3158                               StripQuotes(item.value, item.value);
3159                               if(!strcmp(item.name, "thread-id"))
3160                                  stopItem.threadid = atoi(item.value);
3161                               else if(!strcmp(item.name, "frame"))
3162                               {
3163                                  item.value = StripCurlies(item.value);
3164                                  ParseFrame(stopItem.frame, item.value);
3165                               }
3166                               else if(!strcmp(item.name, "reason"))
3167                                  ;
3168                               else if(!strcmp(item.name, "bkptno"))
3169                                  ;
3170                               else
3171                                  DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3172                            }
3173
3174                            event = stepEnd;
3175                            ide.DebugUpdateMenus(stopped, false);
3176                            ide.Update(null);
3177                         }
3178                         else if(!strcmp(reason, "function-finished"))
3179                         {
3180    #ifdef _DEBUG
3181                            if(stopItem)
3182                               printf("problem\n");
3183    #endif
3184                            stopItem = GdbDataStop { };
3185                            stopItem.reason = CopyString(reason);
3186
3187                            for(i = 2; i < outTokens.count; i++)
3188                            {
3189                               TokenizeListItem(outTokens[i], item);
3190                               StripQuotes(item.value, item.value);
3191                               if(!strcmp(item.name, "thread-id"))
3192                                  stopItem.threadid = atoi(item.value);
3193                               else if(!strcmp(item.name, "frame"))
3194                               {
3195                                  item.value = StripCurlies(item.value);
3196                                  ParseFrame(stopItem.frame, item.value);
3197                               }
3198                               else if(!strcmp(item.name, "gdb-result-var"))
3199                                  stopItem.gdbResultVar = CopyString(item.value);
3200                               else if(!strcmp(item.name, "return-value"))
3201                                  stopItem.returnValue = CopyString(item.value);
3202                               else
3203                                  DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3204                            }
3205
3206                            event = functionEnd;
3207                            ide.DebugUpdateMenus(stopped, false);
3208                            ide.Update(null);
3209                         }
3210                         else if(!strcmp(reason, "signal-received"))
3211                         {
3212    #ifdef _DEBUG
3213                            if(stopItem)
3214                               printf("problem\n");
3215    #endif
3216                            stopItem = GdbDataStop { };
3217                            stopItem.reason = CopyString(reason);
3218
3219                            for(i = 2; i < outTokens.count; i++)
3220                            {
3221                               TokenizeListItem(outTokens[i], item);
3222                               StripQuotes(item.value, item.value);
3223                               if(!strcmp(item.name, "signal-name"))
3224                                  stopItem.name = CopyString(item.value);
3225                               else if(!strcmp(item.name, "signal-meaning"))
3226                                  stopItem.meaning = CopyString(item.value);
3227                               else if(!strcmp(item.name, "thread-id"))
3228                                  stopItem.threadid = atoi(item.value);
3229                               else if(!strcmp(item.name, "frame"))
3230                               {
3231                                  item.value = StripCurlies(item.value);
3232                                  ParseFrame(stopItem.frame, item.value);
3233                               }
3234                               else
3235                                  DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3236                            }
3237                            if(!strcmp(stopItem.name, "SIGTRAP"))
3238                            {
3239                               switch(breakType)
3240                               {
3241                                  case internal:
3242                                     breakType = none;
3243                                     break;
3244                                  case restart:
3245                                  case stop:
3246                                     break;
3247                                  default:
3248                                     ide.DebugUpdateMenus(stopped, false);
3249                                     event = breakEvent;
3250                                     ide.Update(null);
3251                               }
3252                            }
3253                            else
3254                            {
3255                               event = signal;
3256                               ide.DebugUpdateMenus(stopped, false);
3257                               ide.Update(null);
3258                            }
3259                         }
3260                         else if(!strcmp(reason, "watchpoint-trigger"))
3261                            DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3262                         else if(!strcmp(reason, "read-watchpoint-trigger"))
3263                            DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3264                         else if(!strcmp(reason, "access-watchpoint-trigger"))
3265                            DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3266                         else if(!strcmp(reason, "watchpoint-scope"))
3267                            DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3268                         else if(!strcmp(reason, "location-reached"))
3269                            DebuggerProtocolUnknown("Reason location reached not handled", "");
3270                         else
3271                            DebuggerProtocolUnknown("Unknown reason", reason);
3272                      }
3273                   }
3274                   app.SignalEvent();
3275                }
3276             }
3277             else
3278                DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3279             outTokens.RemoveAll();
3280             break;
3281          case '(':
3282             if(!strcmpi(output, "(gdb) "))
3283             {
3284                if(waitingForPID)
3285                {
3286                   char exeFile[MAX_LOCATION];
3287                   int oldProcessID = targetProcessId;
3288                   GetLastDirectory(targetFile, exeFile);
3289
3290                   while(true)
3291                   {
3292                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3293                      if(targetProcessId || gdbHandle.Peek()) break;
3294                      Sleep(0.01);
3295                   }
3296
3297                   if(targetProcessId)
3298                   {
3299                      state = running;
3300                      ide.DebugUpdateMenus(running, false);
3301                      ide.Update(null);
3302                   }
3303                   else if(!oldProcessID)
3304                   {
3305                      ide.outputView.debugBox.Logf("Debugger Error: No target process ID\n");
3306                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3307                      gdbHandle.Printf("-gdb-exit\n");
3308                      gdbTimer.Stop();
3309                      state = terminated; //loaded;
3310                      prjConfig = null;
3311
3312                      if(ide.workspace)
3313                      {
3314                         for(bp : ide.workspace.breakpoints)
3315                            bp.inserted = false;
3316                      }
3317                      for(bp : sysBPs)
3318                         bp.inserted = false;
3319                      if(bpRunToCursor)
3320                         bpRunToCursor.inserted = false;
3321                      
3322                      ide.outputView.debugBox.Logf("Debugging stopped\n");
3323                      ClearBreakDisplay();
3324                      ide.DebugUpdateMenus(loaded, false);
3325                      ide.Update(null);
3326
3327                #if defined(__unix__)
3328                      if(FileExists(progFifoPath)) //fileCreated)
3329                      {
3330                         progThread.terminate = true;
3331                         if(fifoFile)
3332                         {
3333                            fifoFile.CloseInput();
3334                            app.Unlock();
3335                            progThread.Wait();
3336                            app.Lock();
3337                            delete fifoFile;
3338                         }
3339
3340                         DeleteFile(progFifoPath);
3341                         progFifoPath[0] = '\0';
3342                         rmdir(progFifoDir);
3343                      }
3344                #endif
3345                   }
3346                }
3347                gdbReady = true;
3348                serialSemaphore.Release();
3349             }
3350             else
3351                DebuggerProtocolUnknown("Unknown prompt", output);
3352
3353             break;
3354          case '&':
3355             if(!strncmp(output, "&\"warning:", 10))
3356             {
3357                char * content;
3358                content = strstr(output, "\"");
3359                StripQuotes(content, content);
3360                content = strstr(content, ":");
3361                if(content)
3362                   content++;
3363                if(content)
3364                {
3365                   char * s;
3366                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3367                   delete s;
3368                   ide.Update(null);
3369                }
3370             }
3371             break;
3372          default:
3373             DebuggerProtocolUnknown("Unknown output", output);
3374       }
3375       if(!setWaitingForPID)
3376          waitingForPID = false;
3377       setWaitingForPID = false;
3378
3379       delete outTokens;
3380       delete subTokens;
3381       delete item;
3382       delete item2;
3383    }
3384
3385    void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3386    {
3387       if(bpRunToCursor)
3388       {
3389          //bpRunToCursor.Free();
3390          bpRunToCursor = Breakpoint { };
3391       }
3392       else
3393          bpRunToCursor = Breakpoint { };
3394
3395       if(absoluteFilePath)
3396          bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3397       if(relativeFilePath)
3398          bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3399       bpRunToCursor.line = lineNumber;
3400       bpRunToCursor.type = runToCursor;
3401       bpRunToCursor.enabled = true;
3402       bpRunToCursor.condition = null;
3403       bpRunToCursor.ignore = 0;
3404       bpRunToCursor.level = -1;
3405    }
3406
3407    ExpressionType ::DebugEvalExpTypeError(char * result)
3408    {
3409       if(result)
3410          return dummyExp;
3411       switch(eval.error)
3412       {
3413          case symbolNotFound:
3414             return symbolErrorExp;
3415          case memoryCantBeRead:
3416             return memoryErrorExp;
3417       }
3418       return unknownErrorExp;
3419    }
3420
3421    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3422    {
3423       char * result;
3424       if(ide.projectView && ide.debugger.state == stopped)
3425       {
3426          result = GdbEvaluateExpression(expression);
3427          *error = DebugEvalExpTypeError(result);
3428       }
3429       else
3430       {
3431          result = null;
3432          *error = noDebuggerErrorExp;
3433       }
3434       return result;
3435    }
3436
3437    char * ::ReadMemory(uint address, int size, char format, ExpressionType * error)
3438    {
3439       // check for state
3440       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3441       if(!result || !strcmp(result, "N/A"))
3442          *error = memoryErrorExp;
3443       else
3444          *error = DebugEvalExpTypeError(result);
3445       return result;
3446    }
3447 }
3448
3449 class GdbThread : Thread
3450 {
3451    Debugger debugger;
3452
3453    unsigned int Main()
3454    {
3455       static char output[4096];
3456       Array<char> dynamicBuffer { minAllocSize = 4096 };
3457       DualPipe oldGdbHandle = gdbHandle;
3458       incref oldGdbHandle;
3459
3460       app.Lock();
3461       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3462       {
3463          int result;
3464          app.Unlock();
3465          result = gdbHandle.Read(output, 1, sizeof(output));
3466          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3467             break;
3468          app.Lock();
3469          if(result)
3470          {
3471             int c;
3472             int start = 0;
3473
3474             for(c = 0; c<result; c++)
3475             {
3476                if(output[c] == '\n')
3477                {
3478                   int pos = dynamicBuffer.size;
3479                   dynamicBuffer.size += c - start;
3480                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3481                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3482                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3483                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3484                      dynamicBuffer.size++;
3485                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3486 #ifdef _DEBUG
3487                   // printf("%s\n", dynamicBuffer.array);
3488 #endif
3489                   debugger.GdbThreadMain(&dynamicBuffer[0]);
3490                   dynamicBuffer.size = 0;
3491                   start = c + 1;
3492                }
3493             }
3494             if(c == result)
3495             {
3496                int pos = dynamicBuffer.size;
3497                dynamicBuffer.size += c - start;
3498                memcpy(&dynamicBuffer[pos], output + start, c - start);
3499             }
3500          }
3501          else
3502          {
3503 #ifdef _DEBUG
3504             printf("Got end of file from GDB!\n");
3505 #endif
3506          }
3507       }
3508       delete dynamicBuffer;
3509       //if(oldGdbHandle == gdbHandle)
3510          debugger.GdbThreadExit();
3511       delete oldGdbHandle;
3512       app.Unlock();
3513       return 0;
3514    }
3515 }
3516
3517 #if defined(__unix__)
3518 #define uint _uint
3519 #include <errno.h>
3520 #include <stdio.h>
3521 #include <fcntl.h>
3522 #include <sys/types.h>
3523 #undef uint
3524
3525 File fifoFile;
3526
3527 class ProgramThread : Thread
3528 {
3529    bool terminate;
3530    unsigned int Main()
3531    {
3532       bool result = true;
3533       bool fileCreated = false;
3534       mode_t mask = 0600;
3535       static char output[1000];
3536       int fd;
3537
3538       /*if(!mkfifo(progFifoPath, mask))
3539       {
3540          fileCreated = true;
3541       }
3542       else
3543       {
3544          app.Lock();
3545          ide.outputView.debugBox.Logf("err: Unable to create FIFO %s\n", progFifoPath);
3546          app.Unlock();
3547       }*/
3548
3549       if(FileExists(progFifoPath)) //fileCreated)
3550       {
3551          fifoFile = FileOpen(progFifoPath, read);
3552          if(!fifoFile)
3553          {
3554             app.Lock();
3555             ide.outputView.debugBox.Logf("err: Unable to open FIFO %s for read\n", progFifoPath);
3556             app.Unlock();
3557          }
3558          else
3559          {
3560 #ifdef BSD
3561             // TODO: Fix on BSD 
3562             fd = 0;
3563 #else
3564             fd = fileno(fifoFile.input);
3565 #endif
3566             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3567          }
3568       }
3569
3570       while(!terminate && fifoFile && !fifoFile.Eof())
3571       {
3572          fd_set rs, es;
3573          struct timeval time;
3574          int selectResult;
3575          time.tv_sec = 1;
3576          time.tv_usec = 0;
3577          FD_ZERO(&rs);
3578          FD_ZERO(&es);
3579          FD_SET(fd, &rs);
3580          FD_SET(fd, &es);
3581          selectResult = select(fd + 1, &rs, null, null, &time);
3582          if(FD_ISSET(fd, &rs))
3583          {
3584             int result = read(fd, output, sizeof(output)-1);
3585             if(!result || (result < 0 && errno != EAGAIN))
3586                break;
3587             if(result > 0)
3588             {
3589                output[result] = '\0';
3590                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3591                {
3592                   app.Lock();
3593                   ide.outputView.debugBox.Log(output);
3594                   app.Unlock();
3595                }
3596             }
3597          }
3598       }
3599
3600       //if(fifoFile)
3601       {
3602          //fifoFile.CloseInput();
3603          //delete fifoFile;
3604          app.Lock();
3605          ide.outputView.debugBox.Log("\n");
3606          app.Unlock();
3607       }
3608       /*
3609       if(FileExists(progFifoPath)) //fileCreated)
3610       {
3611          DeleteFile(progFifoPath);
3612          progFifoPath[0] = '\0';
3613       }
3614       */
3615       return 0;
3616    }
3617 }
3618 #endif
3619
3620 class Argument : struct
3621 {
3622    Argument prev, next;
3623    char * name;
3624    char * value;
3625
3626    void Free()
3627    {
3628       delete name;
3629       delete value;
3630    }
3631
3632    ~Argument()
3633    {
3634       Free();
3635    }
3636 }
3637
3638 class Frame : struct
3639 {
3640    Frame prev, next;
3641    int level;
3642    char * addr;
3643    char * func;
3644    int argsCount;
3645    OldList args;
3646    char * from;
3647    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3648    char * file;
3649    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3650    char * absoluteFile;
3651    int line;
3652
3653    void Free()
3654    {
3655       delete addr;
3656       delete func;
3657       delete from;
3658       delete file;
3659       delete absoluteFile;
3660       args.Free(Argument::Free);
3661    }
3662
3663    ~Frame()
3664    {
3665       Free();
3666    }
3667 }
3668
3669 class GdbDataStop : struct
3670 {
3671    char * reason;
3672    int threadid;
3673    union
3674    {
3675       struct
3676       {
3677          int bkptno;
3678       };
3679       struct
3680       {
3681          char * name;
3682          char * meaning;
3683       };
3684       struct
3685       {
3686          char * gdbResultVar;
3687          char * returnValue;
3688       };
3689    };
3690    Frame frame { };
3691
3692    void Free()
3693    {
3694       if(reason)
3695       {
3696          if(!strcmp(reason, "signal-received"))
3697          {
3698             delete name;
3699             delete meaning;
3700          }
3701          else if(!strcmp(reason, "function-finished"))
3702          {
3703             delete gdbResultVar;
3704             delete returnValue;
3705          }
3706          delete reason;
3707       }
3708       if(frame) frame.Free();
3709    }
3710
3711    ~GdbDataStop()
3712    {
3713       Free();
3714    }
3715 }
3716
3717 class GdbDataBreakpoint : struct
3718 {
3719    int number;
3720    char * type;
3721    char * disp;
3722    bool enabled;
3723    char * addr;
3724    char * func;
3725    char * file;
3726    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3727    int line;
3728    char * at;
3729    int times;
3730
3731    void Free()
3732    {
3733       delete type;
3734       delete disp;
3735       delete addr;
3736       delete func;
3737       delete file;
3738       delete at;
3739    }
3740
3741    ~GdbDataBreakpoint()
3742    {
3743       Free();
3744    }
3745 }
3746
3747 class Breakpoint : struct
3748 {
3749    class_no_expansion;
3750
3751    char * relativeFilePath;
3752    char * absoluteFilePath;
3753    int line;
3754    bool enabled;
3755    int hits;
3756    int ignore;
3757    int level;
3758    Watch condition;
3759    bool inserted;
3760    BreakpointType type;
3761    DataRow row;
3762    
3763    GdbDataBreakpoint bp;
3764
3765    char * LocationToString()
3766    {
3767       char location[MAX_LOCATION+20];
3768       sprintf(location, "%s:%d", relativeFilePath, line);
3769 #if defined(__WIN32__)
3770       ChangeCh(location, '/', '\\');
3771 #endif
3772       return CopyString(location);
3773    }
3774
3775    void Save(File f)
3776    {
3777       if(relativeFilePath && relativeFilePath[0])
3778       {
3779          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3780          if(condition)
3781             f.Printf("       ~ %s\n", condition.expression);
3782       }
3783    }
3784
3785    void Free()
3786    {
3787       if(bp)
3788          bp.Free();
3789       delete bp;
3790       delete relativeFilePath;
3791       delete absoluteFilePath;
3792    }
3793
3794    ~Breakpoint()
3795    {
3796       Free();
3797    }
3798
3799 }
3800
3801 class Watch : struct
3802 {
3803    class_no_expansion;
3804    
3805    Type type;
3806    char * expression;
3807    char * value;
3808    DataRow row;
3809
3810    void Save(File f)
3811    {
3812       f.Printf("    ~ %s\n", expression);
3813    }
3814
3815    void Free()
3816    {
3817       delete expression;
3818       delete value;
3819       FreeType(type);
3820       type = null;
3821    }
3822
3823    void Reset()
3824    {
3825       delete value;
3826       FreeType(type);
3827       type = null;
3828    }
3829
3830    ~Watch()
3831    {
3832       Free();
3833    }
3834 }
3835
3836 class DebugListItem : struct
3837 {
3838    char * name;
3839    char * value;
3840 }
3841
3842 struct DebugEvaluationData
3843 {
3844    bool active;
3845    char * result;
3846    int bytes;
3847    uint nextBlockAddress;
3848
3849    DebuggerEvaluationError error;
3850 };
3851
3852 class CodeLocation : struct
3853 {
3854    char * file;
3855    char * absoluteFile;
3856    int line;
3857
3858    CodeLocation ::ParseCodeLocation(char * location)
3859    {
3860       if(location)
3861       {
3862          char * colon = null;
3863          char * temp;
3864          char loc[MAX_LOCATION];
3865          strcpy(loc, location);
3866          for(temp = loc; temp = strstr(temp, ":"); temp++)
3867             colon = temp;
3868          if(colon)
3869          {
3870             colon[0] = '\0';
3871             colon++;
3872             if(colon)
3873             {
3874                int line = atoi(colon);
3875                if(line)
3876                {
3877                   CodeLocation codloc { line = line };
3878                   codloc.file = CopyString(loc);
3879                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3880                   return codloc;
3881                }
3882             }
3883          }
3884       }
3885       return null;
3886    }
3887
3888    void Free()
3889    {
3890       delete file;
3891       delete absoluteFile;
3892    }
3893
3894    ~CodeLocation()
3895    {
3896       Free();
3897    }
3898 }