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