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