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