ecere, ide: Replaced integer types by 64 bit ints for tags, ids
[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                            uint 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                            //printf("%x\n", address);
2345                            snprintf(value, sizeof(value), "0x%08x ", address);
2346                            value[sizeof(value)-1] = 0;
2347                            
2348                            if(!address)
2349                               strcat(value, $"Null string");
2350                            else
2351                            {
2352                               int size = 4096;
2353                               len = strlen(value);
2354                               string = null;
2355                               while(!string && size > 2)
2356                               {
2357                                  string = GdbReadMemory(address, size);
2358                                  size /= 2;
2359                               }
2360                               if(string && string[0])
2361                               {
2362                                  value[len++] = '(';
2363                                  if(UTF8Validate(string))
2364                                  {
2365                                     int c;
2366                                     char ch;
2367                                     
2368                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2369                                        value[len++] = ch;                                 
2370                                     value[len++] = ')';
2371                                     value[len++] = '\0';
2372                                     
2373                                  }
2374                                  else
2375                                  {
2376                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2377                                     strcat(value, ") (ISO8859-1)");
2378                                  }
2379
2380                                  delete string;
2381                               }
2382                               else if(string)
2383                               {
2384                                  strcat(value, $"Empty string");
2385                                  delete string;
2386                               }
2387                               else
2388                                  strcat(value, $"Couldn't read memory");
2389                            }
2390                            wh.value = CopyString(value);
2391                         }
2392                      }
2393                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
2394                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
2395                      {
2396                         uint64 value = strtoul(exp.constant, null, 0);
2397                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2398                         EnumClassData enumeration = (EnumClassData)enumClass.data;
2399                         NamedLink item;
2400                         for(item = enumeration.values.first; item; item = item.next)
2401                            if((int)item.data == value)
2402                               break;
2403                         if(item)
2404                            wh.value = CopyString(item.name);
2405                         else
2406                            wh.value = CopyString($"Invalid Enum Value");
2407                         result = (bool)atoi(exp.constant);
2408                      }
2409                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
2410                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2411                      {
2412                         unichar value;
2413                         int signedValue;
2414                         char charString[5];
2415                         char string[256];
2416
2417                         if(exp.constant[0] == '\'')
2418                         {
2419                            if((int)((byte *)exp.constant)[1] > 127)
2420                            {
2421                               int nb;
2422                               value = UTF8GetChar(exp.constant + 1, &nb);
2423                               if(nb < 2) value = exp.constant[1];
2424                               signedValue = value;
2425                            }
2426                            else
2427                            {
2428                               signedValue = exp.constant[1];
2429                               {
2430                                  // Precomp Syntax error with boot strap here:
2431                                  byte b = (byte)(char)signedValue;
2432                                  value = (unichar) b;
2433                               }
2434                            }
2435                         }
2436                         else
2437                         {
2438                            if(wh.type.kind == charType && wh.type.isSigned)
2439                            {
2440                               signedValue = (int)(char)strtol(exp.constant, null, 0);
2441                               {
2442                                  // Precomp Syntax error with boot strap here:
2443                                  byte b = (byte)(char)signedValue;
2444                                  value = (unichar) b;
2445                               }
2446                            }
2447                            else
2448                            {
2449                               value = strtoul(exp.constant, null, 0);
2450                               signedValue = (int)value;
2451                            }
2452                         }
2453                         charString[0] = 0;
2454                         UTF32toUTF8Len(&value, 1, charString, 5);
2455                         if(value == '\0')
2456                            snprintf(string, sizeof(string), "\'\\0' (0)");
2457                         else if(value == '\t')
2458                            snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2459                         else if(value == '\n')
2460                            snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2461                         else if(value == '\r')
2462                            snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2463                         else if(wh.type.kind == charType && wh.type.isSigned)
2464                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2465                         else if(value > 256 || wh.type.kind != charType)
2466                         {
2467                            if(value > 0x10FFFF || !GetCharCategory(value))
2468                               snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2469                            else
2470                               snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2471                         }
2472                         else
2473                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2474                         string[sizeof(string)-1] = 0;
2475                         
2476                         wh.value = CopyString(string);
2477                         result = true;
2478                      }
2479                      else
2480                      {
2481                         wh.value = CopyString(exp.constant);
2482                         result = (bool)atoi(exp.constant);
2483                      }
2484                      break;
2485                   default:
2486                      if(exp.hasAddress)
2487                      {
2488                         wh.value = PrintHexUInt(exp.address);
2489                         result = (bool)exp.address;
2490                      }
2491                      else
2492                      {
2493                         char tempString[256];
2494                         if(exp.member.memberType == propertyMember)
2495                            snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2496                         else
2497                            snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
2498                                  exp.type.OnGetString(tempString, null, null));
2499                      }
2500                      break;
2501                }
2502             }
2503             else
2504                snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2505             if(exp) FreeExpression(exp);
2506
2507             
2508             SetPrivateModule(backupPrivateModule);
2509             SetCurrentContext(backupContext);
2510             SetTopContext(backupContext);
2511             SetGlobalContext(backupContext);
2512             SetThisClass(backupThisClass);
2513          }
2514          //else 
2515          //   wh.value = CopyString("No source file found for selected frame");
2516          
2517          watchmsg[sizeof(watchmsg)-1] = 0;
2518          if(!wh.value)
2519             wh.value = CopyString(watchmsg);
2520       }
2521       ide.watchesView.UpdateWatch(wh);
2522       return result;
2523    }
2524
2525    void EvaluateWatches()
2526    {
2527       for(wh : ide.workspace.watches)
2528          ResolveWatch(wh);
2529    }
2530
2531    char * ::GdbEvaluateExpression(char * expression)
2532    {
2533       eval.active = true;
2534       eval.error = none;
2535       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2536       if(eval.active)
2537          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2538       return eval.result;
2539    }
2540
2541    // to be removed... use GdbReadMemory that returns a byte array instead
2542    char * ::GdbReadMemoryString(uint address, int size, char format, int rows, int cols)
2543    {
2544       eval.active = true;
2545       eval.error = none;
2546 #ifdef _DEBUG
2547       if(!size)
2548          printf("GdbReadMemoryString called with size = 0!\n");
2549 #endif
2550       GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2551       if(eval.active)
2552          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2553       return eval.result;
2554    }
2555
2556    byte * ::GdbReadMemory(uint address, int bytes)
2557    {
2558       eval.active = true;
2559       eval.error = none;
2560       GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2561 #ifdef _DEBUG
2562       if(!bytes)
2563          printf("GdbReadMemory called with bytes = 0!\n");
2564 #endif
2565       if(eval.active)
2566          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2567       else if(eval.result && strcmp(eval.result, "N/A"))
2568       {
2569          byte * result = new byte[bytes];
2570          byte * string = eval.result;
2571          int c = 0;
2572          while(true)
2573          {
2574             result[c++] = (byte)strtol(string, &string, 10);
2575             if(string)
2576             {
2577                if(*string == ',')
2578                   string++;
2579                 else
2580                   break;
2581             }
2582             else
2583                break;
2584          }
2585          return result;
2586       }
2587       return null;
2588    }
2589
2590    void EventHit(GdbDataStop stopItem)
2591    {
2592       bool conditionMet = true;
2593       Breakpoint bp = bpHit;
2594
2595       if(!bp && bpRunToCursor)
2596       {
2597          bp = bpRunToCursor;
2598          if(symbols)
2599             GdbCommand(false, "-break-delete %d", bp.bp.number);
2600       }
2601       
2602       if(bp)
2603       {
2604          if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2605          {
2606             bp.line = stopItem.frame.line;
2607             ide.breakpointsView.UpdateBreakpoint(bp.row);
2608             ide.workspace.Save();
2609          }
2610
2611          switch(bp.type)
2612          {
2613             case internalMain:
2614             case internalWinMain:
2615                GdbBreakpointsInsert();
2616                if(userBreakOnInternBreak)
2617                {
2618                   userBreakOnInternBreak = false;
2619                   // Why was SelectFrame missing here?
2620                   SelectFrame(activeFrameLevel);
2621                   GoToStackFrameLine(activeFrameLevel, true);
2622                   ideMainFrame.Activate();   // TOFIX: ide.Activate() is not reliable (app inactive)
2623                   ide.Update(null);
2624                }
2625                else
2626                   GdbExecContinue(false);
2627                break;
2628             case internalModulesLoaded:
2629                modules = true;
2630                GdbBreakpointsInsert();
2631                GdbExecContinue(false);
2632                break;
2633             case user:
2634             case runToCursor:
2635                if(bp.condition)
2636                   conditionMet = ResolveWatch(bp.condition);
2637                bp.hits++;
2638                if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2639                {
2640                   if(!bp.ignore)
2641                   {
2642                      bp.breaks++;
2643                      ignoreBreakpoints = false;
2644                      // Why was SelectFrame missing here?
2645                      SelectFrame(activeFrameLevel);
2646                      GoToStackFrameLine(activeFrameLevel, true);
2647                      ideMainFrame.Activate();   // TOFIX: ide.Activate() is not reliable (app inactive)
2648                      ide.Update(null);
2649                      if(bp.type == BreakpointType::runToCursor)
2650                      {
2651                         delete bpRunToCursor;
2652                         bpRunToCursor = null;
2653                      }
2654                   }
2655                   else
2656                   {
2657                      bp.ignore--;
2658                      GdbExecContinue(false);
2659                   }
2660                }
2661                else
2662                   GdbExecContinue(false);
2663                ide.breakpointsView.UpdateBreakpoint(bp.row);
2664                break;
2665          }
2666       }
2667       else
2668          ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2669    }
2670
2671    void GdbThreadExit()
2672    {
2673       if(state != terminated)
2674       {
2675          ChangeState(terminated);
2676          targetProcessId = 0;
2677          ClearBreakDisplay();
2678
2679          if(gdbHandle)
2680          {
2681             serialSemaphore.Release();
2682             gdbTimer.Stop();
2683             gdbHandle.Wait();
2684             delete gdbHandle;
2685             
2686             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2687             ide.outputView.debugBox.Logf($"Debugging stopped\n");
2688             ide.Update(null);
2689          }
2690          //ChangeState(terminated);
2691       }
2692    }
2693
2694    void GdbThreadMain(char * output)
2695    {
2696       int i;
2697       Array<char *> outTokens { minAllocSize = 50 };
2698       Array<char *> subTokens { minAllocSize = 50 };
2699       DebugListItem item { };
2700       DebugListItem item2 { };
2701       bool setWaitingForPID = false;
2702       
2703 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2704 #ifdef GDB_DEBUG_CONSOLE
2705       Log(output); Log("\n");
2706 #endif
2707 #ifdef GDB_DEBUG_OUTPUT
2708       {
2709          int len = strlen(output);
2710          if(len > 1024)
2711          {
2712             int c;
2713             char * start;
2714             char tmp[1025];
2715             tmp[1024] = '\0';
2716             start = output;
2717             for(c = 0; c < len / 1024; c++)
2718             {
2719                strncpy(tmp, start, 1024);
2720                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2721                start += 1024;
2722             }
2723             ide.outputView.gdbBox.Logf("out: %s\n", start);
2724          }
2725          else
2726          {
2727             ide.outputView.gdbBox.Logf("out: %s\n", output);
2728          }
2729       }
2730 #endif
2731 #ifdef GDB_DEBUG_CONSOLE
2732          strcpy(lastGdbOutput, output);
2733 #endif
2734 #ifdef GDB_DEBUG_GUI
2735          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2736 #endif
2737 #endif
2738       
2739       switch(output[0])
2740       {
2741          case '~':
2742             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2743             {
2744                symbols = false;
2745                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2746                ide.Update(null);
2747             }
2748             break;
2749          case '^':
2750             gdbReady = false;
2751             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2752             {
2753                //if(outTokens.count == 1)
2754                {
2755                   if(sentKill)
2756                   {
2757                      sentKill = false;
2758                      ChangeState(loaded);
2759                      targetProcessId = 0;
2760                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2761                      {
2762                         if(!strcmp(item.name, "reason"))
2763                         {
2764                            char * reason = item.value;
2765                            StripQuotes(reason, reason);
2766                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2767                            {
2768                               char * exitCode;
2769                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2770                               {
2771                                  StripQuotes(item2.value, item2.value);
2772                                  if(!strcmp(item2.name, "exit-code"))
2773                                     exitCode = item2.value;
2774                                  else
2775                                     exitCode = null;
2776                               }
2777                               else
2778                                  exitCode = null;
2779                               HandleExit(reason, exitCode);
2780                            }
2781                         }
2782                         else
2783                            DebuggerProtocolUnknown("Unknown kill reply", item.name);
2784                      }
2785                      else
2786                         HandleExit(null, null);
2787                   }
2788                }
2789                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2790                {
2791                   if(!strcmp(item.name, "bkpt"))
2792                   {
2793                      sentBreakInsert = false;
2794 #ifdef _DEBUG
2795                      if(bpItem)
2796                         printf("problem\n");
2797 #endif
2798                      bpItem = GdbDataBreakpoint { };
2799                      item.value = StripCurlies(item.value);
2800                      TokenizeList(item.value, ',', subTokens);
2801                      for(i = 0; i < subTokens.count; i++)
2802                      {
2803                         if(TokenizeListItem(subTokens[i], item))
2804                         {
2805                            StripQuotes(item.value, item.value);
2806                            if(!strcmp(item.name, "number"))
2807                               bpItem.number = atoi(item.value);
2808                            else if(!strcmp(item.name, "type"))
2809                               bpItem.type = CopyString(item.value);
2810                            else if(!strcmp(item.name, "disp"))
2811                               bpItem.disp = CopyString(item.value);
2812                            else if(!strcmp(item.name, "enabled"))
2813                               bpItem.enabled = (!strcmpi(item.value, "y"));
2814                            else if(!strcmp(item.name, "addr"))
2815                               bpItem.addr = CopyString(item.value);
2816                            else if(!strcmp(item.name, "func"))
2817                               bpItem.func = CopyString(item.value);
2818                            else if(!strcmp(item.name, "file"))
2819                               bpItem.file = item.value;
2820                            else if(!strcmp(item.name, "line"))
2821                               bpItem.line = atoi(item.value);
2822                            else if(!strcmp(item.name, "at"))
2823                               bpItem.at = CopyString(item.value);
2824                            else if(!strcmp(item.name, "times"))
2825                               bpItem.times = atoi(item.value);
2826                         }
2827                      }
2828                      //breakType = bpValidation;
2829                      //app.SignalEvent();
2830                      subTokens.RemoveAll();
2831                   }
2832                   else if(!strcmp(item.name, "BreakpointTable"))
2833                      ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2834                   else if(!strcmp(item.name, "depth"))
2835                   {
2836                      StripQuotes(item.value, item.value);
2837                      frameCount = atoi(item.value);
2838                      activeFrame = null;
2839                      stackFrames.Free(Frame::Free);
2840                   }
2841                   else if(!strcmp(item.name, "stack"))
2842                   {
2843                      Frame frame;
2844                      if(stackFrames.count)
2845                         ide.callStackView.Logf("...\n");
2846                      else
2847                         activeFrame = null;
2848                      item.value = StripBrackets(item.value);
2849                      TokenizeList(item.value, ',', subTokens);
2850                      for(i = 0; i < subTokens.count; i++)
2851                      {
2852                         if(TokenizeListItem(subTokens[i], item))
2853                         {
2854                            if(!strcmp(item.name, "frame"))
2855                            {
2856                               frame = Frame { };
2857                               stackFrames.Add(frame);
2858                               item.value = StripCurlies(item.value);
2859                               ParseFrame(frame, item.value);
2860                               if(frame.file && frame.from)
2861                                  DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2862                               if(frame.file)
2863                               {
2864                                  char * s;
2865                                  if(activeFrameLevel == -1)
2866                                  {
2867                                     if(ide.projectView.IsModuleInProject(frame.file));
2868                                     {
2869                                        if(frame.level != 0)
2870                                        {
2871                                           //stopItem.frame = frame;
2872                                           breakType = selectFrame;
2873                                        }
2874                                        else
2875                                           activeFrame = frame;
2876                                        activeFrameLevel = frame.level;
2877                                     }
2878                                  }
2879                                  ide.callStackView.Logf("%3d ", frame.level);
2880                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2881                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2882                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2883                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2884                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2885                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2886                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2887                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2888                                  else
2889                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2890                                  delete s;
2891                               }
2892                               else
2893                               {
2894                                  ide.callStackView.Logf("%3d ", frame.level);
2895
2896                                  if(frame.from)
2897                                  {
2898                                     char * s;
2899                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2900                                     delete s;
2901                                  }
2902                                  else if(frame.func)
2903                                     ide.callStackView.Logf("%s\n", frame.func);
2904                                  else
2905                                     ide.callStackView.Logf($"unknown source\n");
2906                               }
2907                            }
2908                            else
2909                               DebuggerProtocolUnknown("Unknown stack content", item.name);
2910                         }
2911                      }
2912                      if(activeFrameLevel == -1)
2913                      {
2914                         activeFrameLevel = 0;
2915                         activeFrame = stackFrames.first;
2916                      }
2917                      ide.callStackView.Home();
2918                      ide.Update(null);
2919                      subTokens.RemoveAll();
2920                   }
2921                   /*else if(!strcmp(item.name, "frame"))
2922                   {
2923                      Frame frame { };
2924                      item.value = StripCurlies(item.value);
2925                      ParseFrame(&frame, item.value);
2926                   }*/
2927                   else if(!strcmp(item.name, "thread-ids"))
2928                   {
2929                      ide.threadsView.Clear();
2930                      item.value = StripCurlies(item.value);
2931                      TokenizeList(item.value, ',', subTokens);
2932                      for(i = subTokens.count - 1; ; i--)
2933                      {
2934                         if(TokenizeListItem(subTokens[i], item))
2935                         {
2936                            if(!strcmp(item.name, "thread-id"))
2937                            {
2938                               int value;
2939                               StripQuotes(item.value, item.value);
2940                               value = atoi(item.value);
2941                               ide.threadsView.Logf("%3d \n", value);
2942                            }
2943                            else
2944                               DebuggerProtocolUnknown("Unknown threads content", item.name);
2945                         }
2946                         if(!i)
2947                            break;
2948                      }
2949                      ide.threadsView.Home();
2950                      ide.Update(null);
2951                      subTokens.RemoveAll();
2952                      //if(!strcmp(outTokens[2], "number-of-threads"))
2953                   }
2954                   else if(!strcmp(item.name, "new-thread-id"))
2955                   {
2956                      StripQuotes(item.value, item.value);
2957                      activeThread = atoi(item.value);
2958                   }
2959                   else if(!strcmp(item.name, "value"))
2960                   {
2961                      StripQuotes(item.value, item.value);
2962                      eval.result = CopyString(item.value);
2963                      eval.active = false;
2964                   }
2965                   else if(!strcmp(item.name, "addr"))
2966                   {
2967                      for(i = 2; i < outTokens.count; i++)
2968                      {
2969                         if(TokenizeListItem(outTokens[i], item))
2970                         {
2971                            if(!strcmp(item.name, "total-bytes"))
2972                            {
2973                               StripQuotes(item.value, item.value);
2974                               eval.bytes = atoi(item.value);
2975                            }
2976                            else if(!strcmp(item.name, "next-row"))
2977                            {
2978                               StripQuotes(item.value, item.value);
2979                               eval.nextBlockAddress = strtoul(item.value, null, 0);
2980                            }
2981                            else if(!strcmp(item.name, "memory"))
2982                            {
2983                               int j;
2984                               //int value;
2985                               //StripQuotes(item.value, item.value);
2986                               item.value = StripBrackets(item.value);
2987                               // this should be treated as a list...
2988                               item.value = StripCurlies(item.value);
2989                               TokenizeList(item.value, ',', subTokens);
2990                               for(j = 0; j < subTokens.count; j++)
2991                               {
2992                                  if(TokenizeListItem(subTokens[j], item))
2993                                  {
2994                                     if(!strcmp(item.name, "data"))
2995                                     {
2996                                        item.value = StripBrackets(item.value);
2997                                        StripQuotes2(item.value, item.value);
2998                                        eval.result = CopyString(item.value);
2999                                        eval.active = false;
3000                                     }
3001                                  }
3002                               }
3003                               subTokens.RemoveAll();
3004                            }
3005                         }
3006                      }
3007                   }
3008                   else if(!strcmp(item.name, "source-path"))
3009                   {
3010                   }
3011                   else
3012                      DebuggerProtocolUnknown("Unknown command reply", item.name);
3013                }
3014             }
3015             else if(!strcmp(outTokens[0], "^running"))
3016             {
3017                waitingForPID = true;
3018                setWaitingForPID = true;
3019             }
3020             else if(!strcmp(outTokens[0], "^exit"))
3021             {
3022                ChangeState(terminated);
3023                // ide.outputView.debugBox.Logf("Exit\n");
3024                // ide.Update(null);
3025                gdbReady = true;
3026                serialSemaphore.Release();
3027             }
3028             else if(!strcmp(outTokens[0], "^error"))
3029             {
3030                if(sentBreakInsert)
3031                {
3032                   sentBreakInsert = false;
3033                   breakpointError = true;
3034 #ifdef _DEBUG
3035                   if(bpItem)
3036                      printf("problem\n");
3037 #endif
3038                   bpItem = GdbDataBreakpoint { };
3039                }
3040
3041                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3042                {
3043                   if(!strcmp(item.name, "msg"))
3044                   {
3045                      StripQuotes(item.value, item.value);
3046                      if(eval.active)
3047                      {
3048                         eval.active = false;
3049                         eval.result = null;
3050                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3051                            eval.error = symbolNotFound;
3052                         else if(strstr(item.value, "Cannot access memory at address"))
3053                            eval.error = memoryCantBeRead;
3054                         else
3055                            eval.error = unknown;
3056                      }
3057                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3058                      {
3059                      }
3060                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3061                      {
3062                      }
3063                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3064                      {
3065                         ChangeState(stopped);
3066                         gdbHandle.Printf("-exec-continue\n");
3067                      }
3068                      else if(!strcmp(item.value, "ptrace: No such process."))
3069                      {
3070                         ChangeState(loaded);
3071                         targetProcessId = 0;
3072                      }
3073                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3074                      {
3075                      }
3076                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3077                      {
3078                         ChangeState(loaded);
3079                         targetProcessId = 0;
3080                      }
3081                      else if(strstr(item.value, "No such file or directory."))
3082                      {
3083                         ChangeState(loaded);
3084                         targetProcessId = 0;
3085                      }
3086                      else if(strstr(item.value, "During startup program exited with code "))
3087                      {
3088                         ChangeState(loaded);
3089                         targetProcessId = 0;
3090                      }
3091                      else
3092                      {
3093 #ifdef _DEBUG
3094                         if(strlen(item.value) < MAX_F_STRING)
3095                         {
3096                            char * s;
3097                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3098                            delete s;
3099                         }
3100                         else
3101                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3102 #endif
3103                      }
3104                   }
3105                }
3106                else
3107                   DebuggerProtocolUnknown("Unknown error content", item.name);
3108             }
3109             else
3110                DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3111             
3112             outTokens.RemoveAll();
3113             break;
3114          case '+':
3115             DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3116             break;
3117          case '=':
3118             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3119                ;
3120             else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3121                ;
3122             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"
3123                ;
3124             else
3125                DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3126             outTokens.RemoveAll();
3127             break;
3128          case '*':
3129             gdbReady = false;
3130             if(TokenizeList(output, ',', outTokens))
3131             {
3132                if(!strcmp(outTokens[0],"*running"))
3133                {
3134                   waitingForPID = true;
3135                   setWaitingForPID = true;
3136                }
3137                else if(!strcmp(outTokens[0], "*stopped"))
3138                {
3139                   int tk;
3140                   ChangeState(stopped);
3141
3142                   for(tk = 1; tk < outTokens.count; tk++)
3143                   {
3144                      if(TokenizeListItem(outTokens[tk], item))
3145                      {
3146                         if(!strcmp(item.name, "reason"))
3147                         {
3148                            char * reason = item.value;
3149                            StripQuotes(reason, reason);
3150                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3151                            {
3152                               char * exitCode;
3153                               if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3154                               {
3155                                  tk++;
3156                                  StripQuotes(item2.value, item2.value);
3157                                  if(!strcmp(item2.name, "exit-code"))
3158                                     exitCode = item2.value;
3159                                  else
3160                                     exitCode = null;
3161                               }
3162                               else
3163                                  exitCode = null;
3164                               HandleExit(reason, exitCode);
3165                            }
3166                            else if(!strcmp(reason, "breakpoint-hit"))
3167                            {
3168       #ifdef _DEBUG
3169                               if(stopItem)
3170                                  printf("problem\n");
3171       #endif
3172                               stopItem = GdbDataStop { };
3173
3174                               for(i = tk+1; i < outTokens.count; i++)
3175                               {
3176                                  TokenizeListItem(outTokens[i], item);
3177                                  StripQuotes(item.value, item.value);
3178                                  if(!strcmp(item.name, "bkptno"))
3179                                     stopItem.bkptno = atoi(item.value);
3180                                  else if(!strcmp(item.name, "thread-id"))
3181                                     stopItem.threadid = atoi(item.value);
3182                                  else if(!strcmp(item.name, "frame"))
3183                                  {
3184                                     item.value = StripCurlies(item.value);
3185                                     ParseFrame(stopItem.frame, item.value);
3186                                  }
3187                                  else
3188                                     DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3189                               }
3190
3191                               event = hit;
3192                            }
3193                            else if(!strcmp(reason, "end-stepping-range"))
3194                            {
3195       #ifdef _DEBUG
3196                               if(stopItem)
3197                                  printf("problem\n");
3198       #endif
3199                               stopItem = GdbDataStop { };
3200
3201                               for(i = tk+1; i < outTokens.count; i++)
3202                               {
3203                                  TokenizeListItem(outTokens[i], item);
3204                                  StripQuotes(item.value, item.value);
3205                                  if(!strcmp(item.name, "thread-id"))
3206                                     stopItem.threadid = atoi(item.value);
3207                                  else if(!strcmp(item.name, "frame"))
3208                                  {
3209                                     item.value = StripCurlies(item.value);
3210                                     ParseFrame(stopItem.frame, item.value);
3211                                  }
3212                                  else if(!strcmp(item.name, "reason"))
3213                                     ;
3214                                  else if(!strcmp(item.name, "bkptno"))
3215                                     ;
3216                                  else
3217                                     DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3218                               }
3219
3220                               event = stepEnd;
3221                               ide.Update(null);
3222                            }
3223                            else if(!strcmp(reason, "function-finished"))
3224                            {
3225       #ifdef _DEBUG
3226                               if(stopItem)
3227                                  printf("problem\n");
3228       #endif
3229                               stopItem = GdbDataStop { };
3230                               stopItem.reason = CopyString(reason);
3231
3232                               for(i = tk+1; i < outTokens.count; i++)
3233                               {
3234                                  TokenizeListItem(outTokens[i], item);
3235                                  StripQuotes(item.value, item.value);
3236                                  if(!strcmp(item.name, "thread-id"))
3237                                     stopItem.threadid = atoi(item.value);
3238                                  else if(!strcmp(item.name, "frame"))
3239                                  {
3240                                     item.value = StripCurlies(item.value);
3241                                     ParseFrame(stopItem.frame, item.value);
3242                                  }
3243                                  else if(!strcmp(item.name, "gdb-result-var"))
3244                                     stopItem.gdbResultVar = CopyString(item.value);
3245                                  else if(!strcmp(item.name, "return-value"))
3246                                     stopItem.returnValue = CopyString(item.value);
3247                                  else
3248                                     DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3249                               }
3250
3251                               event = functionEnd;
3252                               ide.Update(null);
3253                            }
3254                            else if(!strcmp(reason, "signal-received"))
3255                            {
3256       #ifdef _DEBUG
3257                               if(stopItem)
3258                                  printf("problem\n");
3259       #endif
3260                               stopItem = GdbDataStop { };
3261                               stopItem.reason = CopyString(reason);
3262
3263                               for(i = tk+1; i < outTokens.count; i++)
3264                               {
3265                                  TokenizeListItem(outTokens[i], item);
3266                                  StripQuotes(item.value, item.value);
3267                                  if(!strcmp(item.name, "signal-name"))
3268                                     stopItem.name = CopyString(item.value);
3269                                  else if(!strcmp(item.name, "signal-meaning"))
3270                                     stopItem.meaning = CopyString(item.value);
3271                                  else if(!strcmp(item.name, "thread-id"))
3272                                     stopItem.threadid = atoi(item.value);
3273                                  else if(!strcmp(item.name, "frame"))
3274                                  {
3275                                     item.value = StripCurlies(item.value);
3276                                     ParseFrame(stopItem.frame, item.value);
3277                                  }
3278                                  else
3279                                     DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3280                               }
3281                               if(!strcmp(stopItem.name, "SIGTRAP"))
3282                               {
3283                                  switch(breakType)
3284                                  {
3285                                     case internal:
3286                                        breakType = none;
3287                                        break;
3288                                     case restart:
3289                                     case stop:
3290                                        break;
3291                                     default:
3292                                        event = breakEvent;
3293                                  }
3294                               }
3295                               else
3296                               {
3297                                  event = signal;
3298                               }
3299                            }
3300                            else if(!strcmp(reason, "watchpoint-trigger"))
3301                               DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3302                            else if(!strcmp(reason, "read-watchpoint-trigger"))
3303                               DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3304                            else if(!strcmp(reason, "access-watchpoint-trigger"))
3305                               DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3306                            else if(!strcmp(reason, "watchpoint-scope"))
3307                               DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3308                            else if(!strcmp(reason, "location-reached"))
3309                               DebuggerProtocolUnknown("Reason location reached not handled", "");
3310                            else
3311                               DebuggerProtocolUnknown("Unknown reason", reason);
3312                         }
3313                      }
3314                   }
3315                   app.SignalEvent();
3316                }
3317             }
3318             else
3319                DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3320             outTokens.RemoveAll();
3321             break;
3322          case '(':
3323             if(!strcmpi(output, "(gdb) "))
3324             {
3325                if(waitingForPID)
3326                {
3327                   char exeFile[MAX_LOCATION];
3328                   int oldProcessID = targetProcessId;
3329                   GetLastDirectory(targetFile, exeFile);
3330
3331                   while(true)
3332                   {
3333                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3334                      if(targetProcessId || gdbHandle.Peek()) break;
3335                      Sleep(0.01);
3336                   }
3337
3338                   if(targetProcessId)
3339                      ChangeState(running);
3340                   else if(!oldProcessID)
3341                   {
3342                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3343                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3344                      gdbHandle.Printf("-gdb-exit\n");
3345                      gdbTimer.Stop();
3346                      ChangeState(terminated); //loaded;
3347                      prjConfig = null;
3348
3349                      if(ide.workspace)
3350                      {
3351                         for(bp : ide.workspace.breakpoints)
3352                            bp.inserted = false;
3353                      }
3354                      for(bp : sysBPs)
3355                         bp.inserted = false;
3356                      if(bpRunToCursor)
3357                         bpRunToCursor.inserted = false;
3358                      
3359                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3360                      ClearBreakDisplay();
3361
3362                #if defined(__unix__)
3363                      if(FileExists(progFifoPath)) //fileCreated)
3364                      {
3365                         progThread.terminate = true;
3366                         if(fifoFile)
3367                         {
3368                            fifoFile.CloseInput();
3369                            app.Unlock();
3370                            progThread.Wait();
3371                            app.Lock();
3372                            delete fifoFile;
3373                         }
3374
3375                         DeleteFile(progFifoPath);
3376                         progFifoPath[0] = '\0';
3377                         rmdir(progFifoDir);
3378                      }
3379                #endif
3380                   }
3381                }
3382                gdbReady = true;
3383                serialSemaphore.Release();
3384             }
3385             else
3386                DebuggerProtocolUnknown($"Unknown prompt", output);
3387
3388             break;
3389          case '&':
3390             if(!strncmp(output, "&\"warning:", 10))
3391             {
3392                char * content;
3393                content = strstr(output, "\"");
3394                StripQuotes(content, content);
3395                content = strstr(content, ":");
3396                if(content)
3397                   content++;
3398                if(content)
3399                {
3400                   char * s;
3401                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3402                   delete s;
3403                   ide.Update(null);
3404                }
3405             }
3406             break;
3407          default:
3408             DebuggerProtocolUnknown($"Unknown output", output);
3409       }
3410       if(!setWaitingForPID)
3411          waitingForPID = false;
3412       setWaitingForPID = false;
3413
3414       delete outTokens;
3415       delete subTokens;
3416       delete item;
3417       delete item2;
3418    }
3419
3420    void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3421    {
3422       if(bpRunToCursor)
3423       {
3424          //bpRunToCursor.Free();
3425          bpRunToCursor = Breakpoint { };
3426       }
3427       else
3428          bpRunToCursor = Breakpoint { };
3429
3430       if(absoluteFilePath)
3431          bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3432       if(relativeFilePath)
3433          bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3434       bpRunToCursor.line = lineNumber;
3435       bpRunToCursor.type = runToCursor;
3436       bpRunToCursor.enabled = true;
3437       bpRunToCursor.condition = null;
3438       bpRunToCursor.ignore = 0;
3439       bpRunToCursor.level = -1;
3440    }
3441
3442    ExpressionType ::DebugEvalExpTypeError(char * result)
3443    {
3444       if(result)
3445          return dummyExp;
3446       switch(eval.error)
3447       {
3448          case symbolNotFound:
3449             return symbolErrorExp;
3450          case memoryCantBeRead:
3451             return memoryErrorExp;
3452       }
3453       return unknownErrorExp;
3454    }
3455
3456    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3457    {
3458       char * result;
3459       if(ide.projectView && ide.debugger.state == stopped)
3460       {
3461          result = GdbEvaluateExpression(expression);
3462          *error = DebugEvalExpTypeError(result);
3463       }
3464       else
3465       {
3466          result = null;
3467          *error = noDebuggerErrorExp;
3468       }
3469       return result;
3470    }
3471
3472    char * ::ReadMemory(uint address, int size, char format, ExpressionType * error)
3473    {
3474       // check for state
3475       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3476       if(!result || !strcmp(result, "N/A"))
3477          *error = memoryErrorExp;
3478       else
3479          *error = DebugEvalExpTypeError(result);
3480       return result;
3481    }
3482 }
3483
3484 class GdbThread : Thread
3485 {
3486    Debugger debugger;
3487
3488    unsigned int Main()
3489    {
3490       static char output[4096];
3491       Array<char> dynamicBuffer { minAllocSize = 4096 };
3492       DualPipe oldGdbHandle = gdbHandle;
3493       incref oldGdbHandle;
3494
3495       app.Lock();
3496       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3497       {
3498          int result;
3499          app.Unlock();
3500          result = gdbHandle.Read(output, 1, sizeof(output));
3501          app.Lock();
3502          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3503             break;
3504          if(result)
3505          {
3506             int c;
3507             int start = 0;
3508
3509             for(c = 0; c<result; c++)
3510             {
3511                if(output[c] == '\n')
3512                {
3513                   int pos = dynamicBuffer.size;
3514                   dynamicBuffer.size += c - start;
3515                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3516                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3517                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3518                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3519                      dynamicBuffer.size++;
3520                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3521 #ifdef _DEBUG
3522                   // printf("%s\n", dynamicBuffer.array);
3523 #endif
3524                   debugger.GdbThreadMain(&dynamicBuffer[0]);
3525                   dynamicBuffer.size = 0;
3526                   start = c + 1;
3527                }
3528             }
3529             if(c == result)
3530             {
3531                int pos = dynamicBuffer.size;
3532                dynamicBuffer.size += c - start;
3533                memcpy(&dynamicBuffer[pos], output + start, c - start);
3534             }
3535          }
3536          else
3537          {
3538 #ifdef _DEBUG
3539             printf("Got end of file from GDB!\n");
3540 #endif
3541          }
3542       }
3543       delete dynamicBuffer;
3544       //if(oldGdbHandle == gdbHandle)
3545          debugger.GdbThreadExit();
3546       delete oldGdbHandle;
3547       app.Unlock();
3548       return 0;
3549    }
3550 }
3551
3552 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3553 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3554
3555 #if defined(__unix__)
3556 #define uint _uint
3557 #include <errno.h>
3558 #include <stdio.h>
3559 #include <fcntl.h>
3560 #include <sys/types.h>
3561 #undef uint
3562
3563 File fifoFile;
3564
3565 class ProgramThread : Thread
3566 {
3567    bool terminate;
3568    unsigned int Main()
3569    {
3570       bool result = true;
3571       bool fileCreated = false;
3572       mode_t mask = 0600;
3573       static char output[1000];
3574       int fd;
3575
3576       /*if(!mkfifo(progFifoPath, mask))
3577       {
3578          fileCreated = true;
3579       }
3580       else
3581       {
3582          app.Lock();
3583          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3584          app.Unlock();
3585       }*/
3586
3587       if(FileExists(progFifoPath)) //fileCreated)
3588       {
3589          fifoFile = FileOpen(progFifoPath, read);
3590          if(!fifoFile)
3591          {
3592             app.Lock();
3593             ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3594             app.Unlock();
3595          }
3596          else
3597          {
3598             fd = fileno((FILE *)fifoFile.input);
3599             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3600          }
3601       }
3602
3603       while(!terminate && fifoFile && !fifoFile.Eof())
3604       {
3605          fd_set rs, es;
3606          struct timeval time;
3607          int selectResult;
3608          time.tv_sec = 1;
3609          time.tv_usec = 0;
3610          FD_ZERO(&rs);
3611          FD_ZERO(&es);
3612          FD_SET(fd, &rs);
3613          FD_SET(fd, &es);
3614          selectResult = select(fd + 1, &rs, null, null, &time);
3615          if(FD_ISSET(fd, &rs))
3616          {
3617             int result = read(fd, output, sizeof(output)-1);
3618             if(!result || (result < 0 && errno != EAGAIN))
3619                break;
3620             if(result > 0)
3621             {
3622                output[result] = '\0';
3623                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3624                {
3625                   app.Lock();
3626                   ide.outputView.debugBox.Log(output);
3627                   app.Unlock();
3628                }
3629             }
3630          }
3631       }
3632
3633       //if(fifoFile)
3634       {
3635          //fifoFile.CloseInput();
3636          //delete fifoFile;
3637          app.Lock();
3638          ide.outputView.debugBox.Log("\n");
3639          app.Unlock();
3640       }
3641       /*
3642       if(FileExists(progFifoPath)) //fileCreated)
3643       {
3644          DeleteFile(progFifoPath);
3645          progFifoPath[0] = '\0';
3646       }
3647       */
3648       return 0;
3649    }
3650 }
3651 #endif
3652
3653 class Argument : struct
3654 {
3655    Argument prev, next;
3656    char * name;
3657    char * value;
3658
3659    void Free()
3660    {
3661       delete name;
3662       delete value;
3663    }
3664
3665    ~Argument()
3666    {
3667       Free();
3668    }
3669 }
3670
3671 class Frame : struct
3672 {
3673    Frame prev, next;
3674    int level;
3675    char * addr;
3676    char * func;
3677    int argsCount;
3678    OldList args;
3679    char * from;
3680    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3681    char * file;
3682    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3683    char * absoluteFile;
3684    int line;
3685
3686    void Free()
3687    {
3688       delete addr;
3689       delete func;
3690       delete from;
3691       delete file;
3692       delete absoluteFile;
3693       args.Free(Argument::Free);
3694    }
3695
3696    ~Frame()
3697    {
3698       Free();
3699    }
3700 }
3701
3702 class GdbDataStop : struct
3703 {
3704    char * reason;
3705    int threadid;
3706    union
3707    {
3708       struct
3709       {
3710          int bkptno;
3711       };
3712       struct
3713       {
3714          char * name;
3715          char * meaning;
3716       };
3717       struct
3718       {
3719          char * gdbResultVar;
3720          char * returnValue;
3721       };
3722    };
3723    Frame frame { };
3724
3725    void Free()
3726    {
3727       if(reason)
3728       {
3729          if(!strcmp(reason, "signal-received"))
3730          {
3731             delete name;
3732             delete meaning;
3733          }
3734          else if(!strcmp(reason, "function-finished"))
3735          {
3736             delete gdbResultVar;
3737             delete returnValue;
3738          }
3739          delete reason;
3740       }
3741       if(frame) frame.Free();
3742    }
3743
3744    ~GdbDataStop()
3745    {
3746       Free();
3747    }
3748 }
3749
3750 class GdbDataBreakpoint : struct
3751 {
3752    int number;
3753    char * type;
3754    char * disp;
3755    bool enabled;
3756    char * addr;
3757    char * func;
3758    char * file;
3759    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3760    int line;
3761    char * at;
3762    int times;
3763
3764    void Free()
3765    {
3766       delete type;
3767       delete disp;
3768       delete addr;
3769       delete func;
3770       delete file;
3771       delete at;
3772    }
3773
3774    ~GdbDataBreakpoint()
3775    {
3776       Free();
3777    }
3778 }
3779
3780 class Breakpoint : struct
3781 {
3782    class_no_expansion;
3783
3784    char * relativeFilePath;
3785    char * absoluteFilePath;
3786    int line;
3787    bool enabled;
3788    int hits;
3789    int breaks;
3790    int ignore;
3791    int level;
3792    Watch condition;
3793    bool inserted;
3794    BreakpointType type;
3795    DataRow row;
3796    
3797    GdbDataBreakpoint bp;
3798
3799    char * LocationToString()
3800    {
3801       char location[MAX_LOCATION+20];
3802       snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3803       location[sizeof(location)-1] = 0;
3804 #if defined(__WIN32__)
3805       ChangeCh(location, '/', '\\');
3806 #endif
3807       return CopyString(location);
3808    }
3809
3810    void Save(File f)
3811    {
3812       if(relativeFilePath && relativeFilePath[0])
3813       {
3814          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3815          if(condition)
3816             f.Printf("       ~ %s\n", condition.expression);
3817       }
3818    }
3819
3820    void Free()
3821    {
3822       if(bp)
3823          bp.Free();
3824       delete bp;
3825       delete relativeFilePath;
3826       delete absoluteFilePath;
3827    }
3828
3829    ~Breakpoint()
3830    {
3831       Free();
3832    }
3833
3834 }
3835
3836 class Watch : struct
3837 {
3838    class_no_expansion;
3839    
3840    Type type;
3841    char * expression;
3842    char * value;
3843    DataRow row;
3844
3845    void Save(File f)
3846    {
3847       f.Printf("    ~ %s\n", expression);
3848    }
3849
3850    void Free()
3851    {
3852       delete expression;
3853       delete value;
3854       FreeType(type);
3855       type = null;
3856    }
3857
3858    void Reset()
3859    {
3860       delete value;
3861       FreeType(type);
3862       type = null;
3863    }
3864
3865    ~Watch()
3866    {
3867       Free();
3868    }
3869 }
3870
3871 class DebugListItem : struct
3872 {
3873    char * name;
3874    char * value;
3875 }
3876
3877 struct DebugEvaluationData
3878 {
3879    bool active;
3880    char * result;
3881    int bytes;
3882    uint nextBlockAddress;
3883
3884    DebuggerEvaluationError error;
3885 };
3886
3887 class CodeLocation : struct
3888 {
3889    char * file;
3890    char * absoluteFile;
3891    int line;
3892
3893    CodeLocation ::ParseCodeLocation(char * location)
3894    {
3895       if(location)
3896       {
3897          char * colon = null;
3898          char * temp;
3899          char loc[MAX_LOCATION];
3900          strcpy(loc, location);
3901          for(temp = loc; temp = strstr(temp, ":"); temp++)
3902             colon = temp;
3903          if(colon)
3904          {
3905             colon[0] = '\0';
3906             colon++;
3907             if(colon)
3908             {
3909                int line = atoi(colon);
3910                if(line)
3911                {
3912                   CodeLocation codloc { line = line };
3913                   codloc.file = CopyString(loc);
3914                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3915                   return codloc;
3916                }
3917             }
3918          }
3919       }
3920       return null;
3921    }
3922
3923    void Free()
3924    {
3925       delete file;
3926       delete absoluteFile;
3927    }
3928
3929    ~CodeLocation()
3930    {
3931       Free();
3932    }
3933 }