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