ide/buildsystem; default/32/64 bit dropbox. removed old 32/64 bit stuff from compiler...
[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);
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);
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*3];
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 GdbInsertInternalBreakpoint()
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 : ide.workspace.breakpoints)
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          if(deleteRunToCursor && bpRunToCursor)
1670          {
1671             GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1672             bpRunToCursor.inserted = false;
1673             bpRunToCursor.bp = bpItem;
1674             //check here (reply form -break-delete, returns bpitem?)
1675             bpItem = null;
1676          }
1677       }
1678    }
1679
1680    void GdbGetStack()
1681    {
1682       activeFrame = null;
1683       stackFrames.Free(Frame::Free);
1684       GdbCommand(false, "-stack-info-depth");
1685       if(!frameCount)
1686          GdbCommand(false, "-stack-info-depth 192");
1687       if(frameCount && frameCount <= 192)
1688          GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1689       else
1690       {
1691          GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1692          GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1693       }
1694       GdbCommand(false, "");
1695    }
1696
1697    bool GdbTargetSet()
1698    {
1699       if(!targeted)
1700       {
1701          char escaped[MAX_LOCATION];
1702          strescpy(escaped, targetFile);
1703          GdbCommand(false, "file \"%s\"", escaped);  //GDB/MI Missing Implementation -symbol-file, -target-attach
1704
1705          if(!symbols)
1706             return true;
1707
1708          for(prj : ide.workspace.projects)
1709          {
1710             if(prj == ide.workspace.projects.firstIterator.data)
1711                continue;
1712
1713             //PrintLn("THIS: ", (String)prj.topNode.path);
1714             GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1715             //GdbCommand(false, ""); // why this empty GDB command
1716          }
1717
1718          for(dir : ide.workspace.sourceDirs)
1719          {
1720             GdbCommand(false, "-environment-directory \"%s\"", dir);
1721             //GdbCommand(false, ""); // why this empty GDB command
1722          }
1723          GdbInsertInternalBreakpoint();
1724          targeted = true;
1725       }
1726       return true;
1727    }
1728
1729    void GdbTargetRelease()
1730    {
1731       if(targeted)
1732       {
1733          GdbBreakpointsDelete(true);
1734          GdbCommand(false, "file");  //GDB/MI Missing Implementation -target-detach
1735          targeted = false;
1736          symbols = true;
1737       }
1738    }
1739
1740    void GdbDebugBreak(bool internal)
1741    {
1742       if(targetProcessId)
1743       {
1744          if(internal)
1745             breakType = DebuggerAction::internal;
1746
1747          if(ide) ide.Update(null);
1748          app.Unlock();
1749          if(Process_Break(targetProcessId))  //GdbCommand(false, "-exec-interrupt");
1750             serialSemaphore.Wait();
1751          else
1752          {
1753             ChangeState(loaded);
1754             targetProcessId = 0;
1755          }
1756          app.Lock();
1757       }
1758       else
1759          ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1760    }
1761
1762    void GdbExecRun()
1763    {
1764       GdbTargetSet();
1765       GdbExecCommon();
1766       ShowDebuggerViews();
1767       GdbCommand(true, "-exec-run");
1768    }
1769
1770    void GdbExecContinue(bool focus)
1771    {
1772       GdbExecCommon();
1773       GdbCommand(focus, "-exec-continue");
1774    }
1775
1776    void GdbExecNext()
1777    {
1778       GdbExecCommon();
1779       GdbCommand(true, "-exec-next");
1780    }
1781
1782    void GdbExecStep()
1783    {
1784       GdbExecCommon();
1785       GdbCommand(true, "-exec-step");
1786    }
1787
1788    void GdbExecFinish()
1789    {
1790       GdbExecCommon();
1791       GdbCommand(true, "-exec-finish");
1792    }
1793
1794    void GdbExecCommon()
1795    {
1796       ClearBreakDisplay();
1797       GdbBreakpointsInsert();
1798    }
1799
1800 #ifdef GDB_DEBUG_GUI
1801    void SendGDBCommand(char * command)
1802    {
1803       DebuggerState oldState = state;
1804       switch(state)
1805       {
1806          case running:
1807             if(targetProcessId)
1808                GdbDebugBreak(true);
1809          case stopped:
1810          case loaded:
1811             GdbCommand(false, command);
1812             break;
1813       }
1814       if(oldState == running)
1815          GdbExecContinue(false);
1816    }
1817 #endif
1818
1819    void ClearBreakDisplay()
1820    {
1821       activeThread = 0;
1822       activeFrameLevel = -1;
1823       hitThread = 0;
1824       bpHit = null;
1825       signalThread = 0;
1826       signalOn = false;
1827       frameCount = 0;
1828       if(stopItem)
1829          stopItem.Free();
1830       delete stopItem;
1831       event = none;
1832       activeFrame = null;
1833       stackFrames.Free(Frame::Free);
1834       WatchesCodeEditorLinkRelease();
1835       ide.callStackView.Clear();
1836       ide.threadsView.Clear();
1837       ide.Update(null);
1838    }
1839
1840    bool GdbAbortExec()
1841    {
1842       sentKill = true;
1843       GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1844       return true;
1845    }
1846
1847    bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth)
1848    {
1849       bool result = true;
1850       char oldDirectory[MAX_LOCATION];
1851       char tempPath[MAX_LOCATION];
1852       char command[MAX_LOCATION];
1853       Project project = ide.project;
1854       DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
1855       PathBackup pathBackup { };
1856
1857       if(currentCompiler != compiler)
1858       {
1859          delete currentCompiler;
1860          currentCompiler = compiler;
1861          incref currentCompiler;
1862       }
1863       prjConfig = config;
1864       this.bitDepth = bitDepth;
1865
1866       ChangeState(loaded);
1867       sentKill = false;
1868       sentBreakInsert = false;
1869       breakpointError = false;
1870       symbols = true;
1871       targeted = false;
1872       modules = false;
1873       //breakpointsInserted = false;
1874       
1875       ide.outputView.ShowClearSelectTab(debug);
1876       ide.outputView.debugBox.Logf($"Starting debug mode\n");
1877
1878 #ifdef GDB_DEBUG_CONSOLE
1879       Log("Starting GDB"); Log("\n");
1880 #endif
1881 #ifdef GDB_DEBUG_OUTPUT
1882       ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1883 #endif
1884
1885       strcpy(tempPath, ide.workspace.projectDir);
1886       PathCatSlash(tempPath, targetDirExp.dir);
1887       delete targetDir;
1888       targetDir = CopyString(tempPath);
1889       project.CatTargetFileName(tempPath, compiler, config);
1890       delete targetFile;
1891       targetFile = CopyString(tempPath);
1892
1893       GetWorkingDir(oldDirectory, MAX_LOCATION);
1894       if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1895       {
1896          char temp[MAX_LOCATION];
1897          strcpy(temp, ide.workspace.projectDir);
1898          PathCatSlash(temp, ide.workspace.debugDir);
1899          ChangeWorkingDir(temp);
1900       }
1901       else
1902          ChangeWorkingDir(ide.workspace.projectDir);
1903       
1904       ide.SetPath(true, compiler, config, bitDepth);
1905
1906       // TODO: This pollutes the environment, but at least it works
1907       // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1908       // What is the proper solution for this? DualPipeOpenEnv?
1909       // gdb set environment commands don't seem to take effect
1910       for(e : ide.workspace.environmentVars)
1911       {
1912          SetEnvironment(e.name, e.string);
1913       }
1914
1915       strcpy(command,
1916          (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
1917          (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
1918          "gdb");
1919       strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
1920       gdbTimer.Start();
1921       gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1922       if(!gdbHandle)
1923       {
1924          ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
1925          result = false;
1926       }
1927       if(result)
1928       {
1929          incref gdbHandle;
1930          gdbThread.Create();
1931
1932          gdbProcessId = gdbHandle.GetProcessID();
1933          if(!gdbProcessId)
1934          {
1935             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
1936             result = false;
1937          }
1938          if(result)
1939          {
1940             app.Unlock();
1941             serialSemaphore.Wait();
1942             app.Lock();
1943
1944             if(!GdbTargetSet())
1945             {
1946                //ChangeState(terminated);
1947                result = false;
1948             }
1949
1950             if(result)
1951             {
1952 #if defined(__unix__)
1953                {
1954                   CreateTemporaryDir(progFifoDir, "ecereide");
1955                   strcpy(progFifoPath, progFifoDir);
1956                   PathCat(progFifoPath, "ideprogfifo");
1957                   if(!mkfifo(progFifoPath, 0600))
1958                   {
1959                      //fileCreated = true;
1960                   }
1961                   else
1962                   {
1963                      //app.Lock();
1964                      ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
1965                      //app.Unlock();
1966                   }
1967                }
1968
1969                progThread.terminate = false;
1970                progThread.Create();
1971 #endif
1972       
1973 #if defined(__WIN32__)
1974                GdbCommand(false, "-gdb-set new-console on");
1975 #endif
1976          
1977                GdbCommand(false, "-gdb-set verbose off");
1978                //GdbCommand(false, "-gdb-set exec-done-display on");
1979                GdbCommand(false, "-gdb-set step-mode off");
1980                GdbCommand(false, "-gdb-set unwindonsignal on");
1981                //GdbCommand(false, "-gdb-set shell on");
1982                GdbCommand(false, "set print elements 992");
1983                GdbCommand(false, "-gdb-set backtrace limit 100000");
1984
1985 #if defined(__unix__)
1986                GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
1987 #endif
1988
1989                GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
1990                /*
1991                for(e : ide.workspace.environmentVars)
1992                {
1993                   GdbCommand(false, "set environment %s=%s", e.name, e.string);
1994                }
1995                */
1996             }
1997          }
1998       }
1999
2000       ChangeWorkingDir(oldDirectory);
2001
2002       delete pathBackup;
2003
2004       if(!result)
2005          GdbExit();
2006       delete targetDirExp;
2007       return result;
2008    }
2009
2010    void GdbExit()
2011    {
2012       if(gdbHandle && gdbProcessId)
2013       {
2014          GdbCommand(false, "-gdb-exit");
2015
2016          if(gdbThread)
2017          {
2018             app.Unlock();
2019             gdbThread.Wait();
2020             app.Lock();
2021          }
2022          if(gdbHandle)
2023          {
2024             gdbHandle.Wait();
2025             delete gdbHandle;
2026          }
2027       }
2028       gdbTimer.Stop();
2029       ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2030       prjConfig = null;
2031
2032       if(ide.workspace)
2033          for(bp : ide.workspace.breakpoints)
2034             bp.inserted = false;
2035       for(bp : sysBPs)
2036          bp.inserted = false;
2037       if(bpRunToCursor)
2038          bpRunToCursor.inserted = false;
2039       
2040       ide.outputView.debugBox.Logf($"Debugging stopped\n");
2041       ClearBreakDisplay();
2042       ide.Update(null);
2043
2044 #if defined(__unix__)
2045       if(FileExists(progFifoPath)) //fileCreated)
2046       {
2047          progThread.terminate = true;
2048          if(fifoFile)
2049          {
2050             fifoFile.CloseInput();
2051             app.Unlock();
2052             progThread.Wait();
2053             app.Lock();
2054             delete fifoFile;
2055          }         
2056          DeleteFile(progFifoPath);
2057          progFifoPath[0] = '\0';
2058          rmdir(progFifoDir);
2059       }
2060 #endif
2061    }
2062
2063    void WatchesCodeEditorLinkInit()
2064    {
2065       /*
2066       char tempPath[MAX_LOCATION];
2067       char path[MAX_LOCATION];
2068       
2069       //void MakeFilePathProjectRelative(char * path, char * relativePath)
2070       if(!ide.projectView.GetRelativePath(activeFrame.file, tempPath))
2071          strcpy(tempPath, activeFrame.file);
2072       
2073       strcpy(path, ide.workspace.projectDir);
2074       PathCat(path, tempPath);
2075       codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2076       if(!codeEditor)
2077       {
2078          for(srcDir : ide.workspace.sourceDirs)
2079          {
2080             strcpy(path, srcDir);
2081             PathCat(path, tempPath);
2082             codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2083             if(codeEditor) break;
2084          }
2085       }
2086       */
2087
2088       if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2089          activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2090       if(!activeFrame || !activeFrame.absoluteFile)
2091          codeEditor = null;
2092       else
2093          codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal);
2094       if(codeEditor)
2095       {
2096          codeEditor.inUseDebug = true;
2097          incref codeEditor;
2098       }
2099       //watchesInit = true;
2100    }
2101
2102    void WatchesCodeEditorLinkRelease()
2103    {
2104       //if(watchesInit)
2105       {
2106          if(codeEditor)
2107          {
2108             codeEditor.inUseDebug = false;
2109             if(!codeEditor.visible)
2110                codeEditor.Destroy(0);
2111             delete codeEditor;
2112          }
2113       }
2114    }
2115
2116    bool ResolveWatch(Watch wh)
2117    {
2118       bool result = false;
2119       
2120       wh.Reset();
2121
2122       /*delete wh.value;
2123       if(wh.type) 
2124       {
2125          FreeType(wh.type);
2126          wh.type = null;
2127       }*/
2128
2129       if(wh.expression)
2130       {
2131          char watchmsg[MAX_F_STRING];
2132          if(state == stopped && !codeEditor)
2133             wh.value = CopyString($"No source file found for selected frame");
2134          //if(codeEditor && state == stopped || state != stopped)
2135          else
2136          {
2137             Module backupPrivateModule;
2138             Context backupContext;
2139             Class backupThisClass;
2140             Expression exp;
2141             parseError = false;
2142
2143             backupPrivateModule = GetPrivateModule();
2144             backupContext = GetCurrentContext();
2145             backupThisClass = GetThisClass();
2146             if(codeEditor)
2147             {
2148                SetPrivateModule(codeEditor.privateModule);
2149                SetCurrentContext(codeEditor.globalContext);
2150                SetTopContext(codeEditor.globalContext);
2151                SetGlobalContext(codeEditor.globalContext);
2152                SetGlobalData(&codeEditor.globalData);
2153             }
2154          
2155             exp = ParseExpressionString(wh.expression);
2156             
2157             if(exp && !parseError)
2158             {
2159                if(GetPrivateModule())
2160                {
2161                   if(codeEditor)
2162                      DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2163                   ProcessExpressionType(exp);
2164                }
2165                wh.type = exp.expType;
2166                if(wh.type)
2167                   wh.type.refCount++;
2168                DebugComputeExpression(exp);
2169
2170                /*if(exp.hasAddress)
2171                {
2172                   char temp[MAX_F_STRING];
2173                   sprintf(temp, "0x%x", exp.address);
2174                   wh.address = CopyString(temp);
2175                   // wh.address = CopyStringf("0x%x", exp.address);
2176                }*/
2177 /*
2178 //#ifdef _DEBUG
2179                {
2180                   Type dataType = exp.expType;
2181                   if(dataType)
2182                   {
2183                      char temp[MAX_F_STRING];
2184                      switch(dataType.kind)
2185                      {
2186                         case charType:
2187                            sprintf(temp, "%i", exp.val.c);
2188                            break;
2189                         case shortType:
2190                            sprintf(temp, "%i", exp.val.s);
2191                            break;
2192                         case intType:
2193                         case longType:
2194                         case enumType:
2195                            sprintf(temp, "%i", exp.val.i);
2196                            break;
2197                         case int64Type:
2198                            sprintf(temp, "%i", exp.val.i64);
2199                            break;
2200                         case pointerType:
2201                            sprintf(temp, "%i", exp.val.p);
2202                            break;
2203
2204                         case floatType:
2205                         {
2206                            long v = (long)exp.val.f;
2207                            sprintf(temp, "%i", v);
2208                            break;
2209                         } 
2210                         case doubleType:
2211                         {
2212                            long v = (long)exp.val.d;
2213                            sprintf(temp, "%i", v);
2214                            break;
2215                         }
2216                      }
2217                      if(temp)
2218                         wh.intVal = CopyString(temp);
2219                      switch(dataType.kind)
2220                      {
2221                         case charType:
2222                            sprintf(temp, "0x%x", exp.val.c);
2223                            break;
2224                         case shortType:
2225                            sprintf(temp, "0x%x", exp.val.s);
2226                            break;
2227                         case enumType:
2228                         case intType:
2229                            sprintf(temp, "0x%x", exp.val.i);
2230                            break;
2231                         case int64Type:
2232                            sprintf(temp, "0x%x", exp.val.i64);
2233                            break;
2234                         case longType:
2235                            sprintf(temp, "0x%x", exp.val.i64);
2236                            break;
2237                         case pointerType:
2238                            sprintf(temp, "0x%x", exp.val.p);
2239                            break;
2240
2241                         case floatType:
2242                         {
2243                            long v = (long)exp.val.f;
2244                            sprintf(temp, "0x%x", v);
2245                            break;
2246                         } 
2247                         case doubleType:
2248                         {
2249                            long v = (long)exp.val.d;
2250                            sprintf(temp, "0x%x", v);
2251                            break;
2252                         }
2253                      }
2254                      if(temp)
2255                         wh.hexVal = CopyString(temp);
2256                      switch(dataType.kind)
2257                      {
2258                         case charType:
2259                            sprintf(temp, "0o%o", exp.val.c);
2260                            break;
2261                         case shortType:
2262                            sprintf(temp, "0o%o", exp.val.s);
2263                            break;
2264                         case enumType:
2265                         case intType:
2266                            sprintf(temp, "0o%o", exp.val.i);
2267                            break;
2268                         case int64Type:
2269                            sprintf(temp, "0o%o", exp.val.i64);
2270                            break;
2271                         case longType:
2272                            sprintf(temp, "0o%o", exp.val.i64);
2273                            break;
2274                         case pointerType:
2275                            sprintf(temp, "0o%o", exp.val.p);
2276                            break;
2277
2278                         case floatType:
2279                         {
2280                            long v = (long)exp.val.f;
2281                            sprintf(temp, "0o%o", v);
2282                            break;
2283                         } 
2284                         case doubleType:
2285                         {
2286                            long v = (long)exp.val.d;
2287                            sprintf(temp, "0o%o", v);
2288                            break;
2289                         }
2290                      }
2291                      if(temp)
2292                         wh.octVal = CopyString(temp);
2293                   }
2294                }
2295                // WHATS THIS HERE ?
2296                if(exp.type == constantExp && exp.constant)
2297                   wh.constant = CopyString(exp.constant);
2298 //#endif
2299 */
2300
2301                switch(exp.type)
2302                {
2303                   case symbolErrorExp:
2304                      snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2305                      break;
2306                   case structMemberSymbolErrorExp:
2307                      // todo get info as in next case (ExpClassMemberSymbolError)
2308                      snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2309                      break;
2310                   case classMemberSymbolErrorExp:
2311                      {
2312                         Class _class;
2313                         Expression memberExp = exp.member.exp;
2314                         Identifier memberID = exp.member.member;
2315                         Type type = memberExp.expType;
2316                         if(type)
2317                         {
2318                            _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2319                            if(!_class)
2320                            {
2321                               char string[256] = "";
2322                               Symbol classSym;
2323                               PrintType(type, string, false, true);
2324                               classSym = FindClass(string);
2325                               _class = classSym ? classSym.registered : null;
2326                            }
2327                            if(_class)
2328                               snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2329                            else
2330                               snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2331                         }
2332                         else
2333                            snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2334                      }
2335                      break;
2336                   case memoryErrorExp:
2337                      // Need to ensure when set to memoryErrorExp, constant is set
2338                      snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2339                      break;
2340                   case dereferenceErrorExp:
2341                      snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2342                      break;
2343                   case unknownErrorExp:
2344                      snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2345                      break;
2346                   case noDebuggerErrorExp:
2347                      snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2348                      break;
2349                   case debugStateErrorExp:
2350                      snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2351                      break;
2352                   case 0:
2353                      snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2354                      break;
2355                   case constantExp:
2356                   case stringExp:
2357                      // Temporary Code for displaying Strings
2358                      if((exp.expType && ((exp.expType.kind == pointerType || 
2359                               exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) || 
2360                            (wh.type && wh.type.kind == classType && wh.type._class && 
2361                               wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2362                               !strcmp(wh.type._class.registered.name, "String")))
2363                      {
2364
2365                         if(exp.expType.kind != arrayType || exp.hasAddress)
2366                         {
2367                            uint64 address;
2368                            char * string;
2369                            char value[4196];
2370                            int len;
2371                            //char temp[MAX_F_STRING * 32];
2372
2373                            ExpressionType evalError = dummyExp;
2374                            /*if(exp.expType.kind == arrayType)
2375                               sprintf(temp, "(char*)0x%x", exp.address);
2376                            else
2377                               sprintf(temp, "(char*)%s", exp.constant);*/
2378
2379                            //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2380                            // address = strtoul(exp.constant, null, 0);
2381                            address = _strtoui64(exp.constant, null, 0);
2382                            //printf("%x\n", address);
2383                            // snprintf(value, sizeof(value), "0x%08x ", address);
2384
2385                            if(address > 0xFFFFFFFFLL)
2386                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2387                            else
2388                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2389                            value[sizeof(value)-1] = 0;
2390                            
2391                            if(!address)
2392                               strcat(value, $"Null string");
2393                            else
2394                            {
2395                               int size = 4096;
2396                               len = strlen(value);
2397                               string = null;
2398                               while(!string && size > 2)
2399                               {
2400                                  string = GdbReadMemory(address, size);
2401                                  size /= 2;
2402                               }
2403                               if(string && string[0])
2404                               {
2405                                  value[len++] = '(';
2406                                  if(UTF8Validate(string))
2407                                  {
2408                                     int c;
2409                                     char ch;
2410                                     
2411                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2412                                        value[len++] = ch;                                 
2413                                     value[len++] = ')';
2414                                     value[len++] = '\0';
2415                                     
2416                                  }
2417                                  else
2418                                  {
2419                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2420                                     strcat(value, ") (ISO8859-1)");
2421                                  }
2422
2423                                  delete string;
2424                               }
2425                               else if(string)
2426                               {
2427                                  strcat(value, $"Empty string");
2428                                  delete string;
2429                               }
2430                               else
2431                                  strcat(value, $"Couldn't read memory");
2432                            }
2433                            wh.value = CopyString(value);
2434                         }
2435                      }
2436                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
2437                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
2438                      {
2439                         uint64 value = strtoul(exp.constant, null, 0);
2440                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2441                         EnumClassData enumeration = (EnumClassData)enumClass.data;
2442                         NamedLink item;
2443                         for(item = enumeration.values.first; item; item = item.next)
2444                            if((int)item.data == value)
2445                               break;
2446                         if(item)
2447                            wh.value = CopyString(item.name);
2448                         else
2449                            wh.value = CopyString($"Invalid Enum Value");
2450                         result = (bool)atoi(exp.constant);
2451                      }
2452                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
2453                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2454                      {
2455                         unichar value;
2456                         int signedValue;
2457                         char charString[5];
2458                         char string[256];
2459
2460                         if(exp.constant[0] == '\'')
2461                         {
2462                            if((int)((byte *)exp.constant)[1] > 127)
2463                            {
2464                               int nb;
2465                               value = UTF8GetChar(exp.constant + 1, &nb);
2466                               if(nb < 2) value = exp.constant[1];
2467                               signedValue = value;
2468                            }
2469                            else
2470                            {
2471                               signedValue = exp.constant[1];
2472                               {
2473                                  // Precomp Syntax error with boot strap here:
2474                                  byte b = (byte)(char)signedValue;
2475                                  value = (unichar) b;
2476                               }
2477                            }
2478                         }
2479                         else
2480                         {
2481                            if(wh.type.kind == charType && wh.type.isSigned)
2482                            {
2483                               signedValue = (int)(char)strtol(exp.constant, null, 0);
2484                               {
2485                                  // Precomp Syntax error with boot strap here:
2486                                  byte b = (byte)(char)signedValue;
2487                                  value = (unichar) b;
2488                               }
2489                            }
2490                            else
2491                            {
2492                               value = strtoul(exp.constant, null, 0);
2493                               signedValue = (int)value;
2494                            }
2495                         }
2496                         charString[0] = 0;
2497                         UTF32toUTF8Len(&value, 1, charString, 5);
2498                         if(value == '\0')
2499                            snprintf(string, sizeof(string), "\'\\0' (0)");
2500                         else if(value == '\t')
2501                            snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2502                         else if(value == '\n')
2503                            snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2504                         else if(value == '\r')
2505                            snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2506                         else if(wh.type.kind == charType && wh.type.isSigned)
2507                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2508                         else if(value > 256 || wh.type.kind != charType)
2509                         {
2510                            if(value > 0x10FFFF || !GetCharCategory(value))
2511                               snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2512                            else
2513                               snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2514                         }
2515                         else
2516                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2517                         string[sizeof(string)-1] = 0;
2518                         
2519                         wh.value = CopyString(string);
2520                         result = true;
2521                      }
2522                      else
2523                      {
2524                         wh.value = CopyString(exp.constant);
2525                         result = (bool)atoi(exp.constant);
2526                      }
2527                      break;
2528                   default:
2529                      if(exp.hasAddress)
2530                      {
2531                         wh.value = PrintHexUInt64(exp.address);
2532                         result = (bool)exp.address;
2533                      }
2534                      else
2535                      {
2536                         char tempString[256];
2537                         if(exp.member.memberType == propertyMember)
2538                            snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2539                         else
2540                            snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
2541                                  exp.type.OnGetString(tempString, null, null));
2542                      }
2543                      break;
2544                }
2545             }
2546             else
2547                snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2548             if(exp) FreeExpression(exp);
2549
2550             
2551             SetPrivateModule(backupPrivateModule);
2552             SetCurrentContext(backupContext);
2553             SetTopContext(backupContext);
2554             SetGlobalContext(backupContext);
2555             SetThisClass(backupThisClass);
2556          }
2557          //else 
2558          //   wh.value = CopyString("No source file found for selected frame");
2559          
2560          watchmsg[sizeof(watchmsg)-1] = 0;
2561          if(!wh.value)
2562             wh.value = CopyString(watchmsg);
2563       }
2564       ide.watchesView.UpdateWatch(wh);
2565       return result;
2566    }
2567
2568    void EvaluateWatches()
2569    {
2570       for(wh : ide.workspace.watches)
2571          ResolveWatch(wh);
2572    }
2573
2574    char * ::GdbEvaluateExpression(char * expression)
2575    {
2576       eval.active = true;
2577       eval.error = none;
2578       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2579       if(eval.active)
2580          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2581       return eval.result;
2582    }
2583
2584    // to be removed... use GdbReadMemory that returns a byte array instead
2585    char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2586    {
2587       eval.active = true;
2588       eval.error = none;
2589 #ifdef _DEBUG
2590       if(!size)
2591          printf("GdbReadMemoryString called with size = 0!\n");
2592 #endif
2593       // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2594       if(GetRuntimePlatform() == win32)
2595          GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2596       else
2597          GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2598       if(eval.active)
2599          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2600       return eval.result;
2601    }
2602
2603    byte * ::GdbReadMemory(uint64 address, int bytes)
2604    {
2605       eval.active = true;
2606       eval.error = none;
2607       //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2608       if(GetRuntimePlatform() == win32)
2609          GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2610       else
2611          GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2612 #ifdef _DEBUG
2613       if(!bytes)
2614          printf("GdbReadMemory called with bytes = 0!\n");
2615 #endif
2616       if(eval.active)
2617          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2618       else if(eval.result && strcmp(eval.result, "N/A"))
2619       {
2620          byte * result = new byte[bytes];
2621          byte * string = eval.result;
2622          int c = 0;
2623          while(true)
2624          {
2625             result[c++] = (byte)strtol(string, &string, 10);
2626             if(string)
2627             {
2628                if(*string == ',')
2629                   string++;
2630                 else
2631                   break;
2632             }
2633             else
2634                break;
2635          }
2636          return result;
2637       }
2638       return null;
2639    }
2640
2641    void EventHit(GdbDataStop stopItem)
2642    {
2643       bool conditionMet = true;
2644       Breakpoint bp = bpHit;
2645
2646       if(!bp && bpRunToCursor)
2647       {
2648          bp = bpRunToCursor;
2649          if(symbols)
2650             GdbCommand(false, "-break-delete %d", bp.bp.number);
2651       }
2652       
2653       if(bp)
2654       {
2655          if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2656          {
2657             bp.line = stopItem.frame.line;
2658             ide.breakpointsView.UpdateBreakpoint(bp.row);
2659             ide.workspace.Save();
2660          }
2661
2662          switch(bp.type)
2663          {
2664             case internalMain:
2665             case internalWinMain:
2666                GdbBreakpointsInsert();
2667                if(userBreakOnInternBreak)
2668                {
2669                   userBreakOnInternBreak = false;
2670                   // Why was SelectFrame missing here?
2671                   SelectFrame(activeFrameLevel);
2672                   GoToStackFrameLine(activeFrameLevel, true);
2673                   ideMainFrame.Activate();   // TOFIX: ide.Activate() is not reliable (app inactive)
2674                   ide.Update(null);
2675                }
2676                else
2677                   GdbExecContinue(false);
2678                break;
2679             case internalModulesLoaded:
2680                modules = true;
2681                GdbInsertInternalBreakpoint();
2682                GdbBreakpointsInsert();
2683                GdbExecContinue(false);
2684                break;
2685             case internalModuleLoad:
2686                GdbBreakpointsInsert();
2687                GdbExecContinue(false);
2688                break;
2689             case user:
2690             case runToCursor:
2691                if(bp.condition)
2692                   conditionMet = ResolveWatch(bp.condition);
2693                bp.hits++;
2694                if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2695                {
2696                   if(!bp.ignore)
2697                   {
2698                      bp.breaks++;
2699                      ignoreBreakpoints = false;
2700                      // Why was SelectFrame missing here?
2701                      SelectFrame(activeFrameLevel);
2702                      GoToStackFrameLine(activeFrameLevel, true);
2703                      ideMainFrame.Activate();   // TOFIX: ide.Activate() is not reliable (app inactive)
2704                      ide.Update(null);
2705                      if(bp.type == BreakpointType::runToCursor)
2706                      {
2707                         delete bpRunToCursor;
2708                         bpRunToCursor = null;
2709                      }
2710                   }
2711                   else
2712                   {
2713                      bp.ignore--;
2714                      GdbExecContinue(false);
2715                   }
2716                }
2717                else
2718                   GdbExecContinue(false);
2719                ide.breakpointsView.UpdateBreakpoint(bp.row);
2720                break;
2721          }
2722       }
2723       else
2724          ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2725    }
2726
2727    void GdbThreadExit()
2728    {
2729       if(state != terminated)
2730       {
2731          ChangeState(terminated);
2732          targetProcessId = 0;
2733          ClearBreakDisplay();
2734
2735          if(gdbHandle)
2736          {
2737             serialSemaphore.Release();
2738             gdbTimer.Stop();
2739             gdbHandle.Wait();
2740             delete gdbHandle;
2741             
2742             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2743             ide.outputView.debugBox.Logf($"Debugging stopped\n");
2744             ide.Update(null);
2745          }
2746          //ChangeState(terminated);
2747       }
2748    }
2749
2750    void GdbThreadMain(char * output)
2751    {
2752       int i;
2753       Array<char *> outTokens { minAllocSize = 50 };
2754       Array<char *> subTokens { minAllocSize = 50 };
2755       DebugListItem item { };
2756       DebugListItem item2 { };
2757       bool setWaitingForPID = false;
2758       
2759 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2760 #ifdef GDB_DEBUG_CONSOLE
2761       Log(output); Log("\n");
2762 #endif
2763 #ifdef GDB_DEBUG_OUTPUT
2764       {
2765          int len = strlen(output);
2766          if(len > 1024)
2767          {
2768             int c;
2769             char * start;
2770             char tmp[1025];
2771             tmp[1024] = '\0';
2772             start = output;
2773             for(c = 0; c < len / 1024; c++)
2774             {
2775                strncpy(tmp, start, 1024);
2776                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2777                start += 1024;
2778             }
2779             ide.outputView.gdbBox.Logf("out: %s\n", start);
2780          }
2781          else
2782          {
2783             ide.outputView.gdbBox.Logf("out: %s\n", output);
2784          }
2785       }
2786 #endif
2787 #ifdef GDB_DEBUG_CONSOLE
2788          strcpy(lastGdbOutput, output);
2789 #endif
2790 #ifdef GDB_DEBUG_GUI
2791          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2792 #endif
2793 #endif
2794       
2795       switch(output[0])
2796       {
2797          case '~':
2798             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2799             {
2800                symbols = false;
2801                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2802                ide.Update(null);
2803             }
2804             break;
2805          case '^':
2806             gdbReady = false;
2807             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2808             {
2809                //if(outTokens.count == 1)
2810                {
2811                   if(sentKill)
2812                   {
2813                      sentKill = false;
2814                      ChangeState(loaded);
2815                      targetProcessId = 0;
2816                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2817                      {
2818                         if(!strcmp(item.name, "reason"))
2819                         {
2820                            char * reason = item.value;
2821                            StripQuotes(reason, reason);
2822                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2823                            {
2824                               char * exitCode;
2825                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2826                               {
2827                                  StripQuotes(item2.value, item2.value);
2828                                  if(!strcmp(item2.name, "exit-code"))
2829                                     exitCode = item2.value;
2830                                  else
2831                                     exitCode = null;
2832                               }
2833                               else
2834                                  exitCode = null;
2835                               HandleExit(reason, exitCode);
2836                            }
2837                         }
2838                         else
2839                            DebuggerProtocolUnknown("Unknown kill reply", item.name);
2840                      }
2841                      else
2842                         HandleExit(null, null);
2843                   }
2844                }
2845                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2846                {
2847                   if(!strcmp(item.name, "bkpt"))
2848                   {
2849                      sentBreakInsert = false;
2850 #ifdef _DEBUG
2851                      if(bpItem)
2852                         printf("problem\n");
2853 #endif
2854                      bpItem = GdbDataBreakpoint { };
2855                      item.value = StripCurlies(item.value);
2856                      TokenizeList(item.value, ',', subTokens);
2857                      for(i = 0; i < subTokens.count; i++)
2858                      {
2859                         if(TokenizeListItem(subTokens[i], item))
2860                         {
2861                            StripQuotes(item.value, item.value);
2862                            if(!strcmp(item.name, "number"))
2863                               bpItem.number = atoi(item.value);
2864                            else if(!strcmp(item.name, "type"))
2865                               bpItem.type = CopyString(item.value);
2866                            else if(!strcmp(item.name, "disp"))
2867                               bpItem.disp = CopyString(item.value);
2868                            else if(!strcmp(item.name, "enabled"))
2869                               bpItem.enabled = (!strcmpi(item.value, "y"));
2870                            else if(!strcmp(item.name, "addr"))
2871                               bpItem.addr = CopyString(item.value);
2872                            else if(!strcmp(item.name, "func"))
2873                               bpItem.func = CopyString(item.value);
2874                            else if(!strcmp(item.name, "file"))
2875                               bpItem.file = item.value;
2876                            else if(!strcmp(item.name, "line"))
2877                               bpItem.line = atoi(item.value);
2878                            else if(!strcmp(item.name, "at"))
2879                               bpItem.at = CopyString(item.value);
2880                            else if(!strcmp(item.name, "times"))
2881                               bpItem.times = atoi(item.value);
2882                         }
2883                      }
2884                      //breakType = bpValidation;
2885                      //app.SignalEvent();
2886                      subTokens.RemoveAll();
2887                   }
2888                   else if(!strcmp(item.name, "BreakpointTable"))
2889                      ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2890                   else if(!strcmp(item.name, "depth"))
2891                   {
2892                      StripQuotes(item.value, item.value);
2893                      frameCount = atoi(item.value);
2894                      activeFrame = null;
2895                      stackFrames.Free(Frame::Free);
2896                   }
2897                   else if(!strcmp(item.name, "stack"))
2898                   {
2899                      Frame frame;
2900                      if(stackFrames.count)
2901                         ide.callStackView.Logf("...\n");
2902                      else
2903                         activeFrame = null;
2904                      item.value = StripBrackets(item.value);
2905                      TokenizeList(item.value, ',', subTokens);
2906                      for(i = 0; i < subTokens.count; i++)
2907                      {
2908                         if(TokenizeListItem(subTokens[i], item))
2909                         {
2910                            if(!strcmp(item.name, "frame"))
2911                            {
2912                               frame = Frame { };
2913                               stackFrames.Add(frame);
2914                               item.value = StripCurlies(item.value);
2915                               ParseFrame(frame, item.value);
2916                               if(frame.file && frame.from)
2917                                  DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2918                               if(frame.file)
2919                               {
2920                                  char * s;
2921                                  if(activeFrameLevel == -1)
2922                                  {
2923                                     if(ide.projectView.IsModuleInProject(frame.file));
2924                                     {
2925                                        if(frame.level != 0)
2926                                        {
2927                                           //stopItem.frame = frame;
2928                                           breakType = selectFrame;
2929                                        }
2930                                        else
2931                                           activeFrame = frame;
2932                                        activeFrameLevel = frame.level;
2933                                     }
2934                                  }
2935                                  ide.callStackView.Logf("%3d ", frame.level);
2936                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2937                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2938                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2939                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2940                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2941                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2942                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2943                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2944                                  else
2945                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2946                                  delete s;
2947                               }
2948                               else
2949                               {
2950                                  ide.callStackView.Logf("%3d ", frame.level);
2951
2952                                  if(frame.from)
2953                                  {
2954                                     char * s;
2955                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2956                                     delete s;
2957                                  }
2958                                  else if(frame.func)
2959                                     ide.callStackView.Logf("%s\n", frame.func);
2960                                  else
2961                                     ide.callStackView.Logf($"unknown source\n");
2962                               }
2963                            }
2964                            else
2965                               DebuggerProtocolUnknown("Unknown stack content", item.name);
2966                         }
2967                      }
2968                      if(activeFrameLevel == -1)
2969                      {
2970                         activeFrameLevel = 0;
2971                         activeFrame = stackFrames.first;
2972                      }
2973                      ide.callStackView.Home();
2974                      ide.Update(null);
2975                      subTokens.RemoveAll();
2976                   }
2977                   /*else if(!strcmp(item.name, "frame"))
2978                   {
2979                      Frame frame { };
2980                      item.value = StripCurlies(item.value);
2981                      ParseFrame(&frame, item.value);
2982                   }*/
2983                   else if(!strcmp(item.name, "thread-ids"))
2984                   {
2985                      ide.threadsView.Clear();
2986                      item.value = StripCurlies(item.value);
2987                      TokenizeList(item.value, ',', subTokens);
2988                      for(i = subTokens.count - 1; ; i--)
2989                      {
2990                         if(TokenizeListItem(subTokens[i], item))
2991                         {
2992                            if(!strcmp(item.name, "thread-id"))
2993                            {
2994                               int value;
2995                               StripQuotes(item.value, item.value);
2996                               value = atoi(item.value);
2997                               ide.threadsView.Logf("%3d \n", value);
2998                            }
2999                            else
3000                               DebuggerProtocolUnknown("Unknown threads content", item.name);
3001                         }
3002                         if(!i)
3003                            break;
3004                      }
3005                      ide.threadsView.Home();
3006                      ide.Update(null);
3007                      subTokens.RemoveAll();
3008                      //if(!strcmp(outTokens[2], "number-of-threads"))
3009                   }
3010                   else if(!strcmp(item.name, "new-thread-id"))
3011                   {
3012                      StripQuotes(item.value, item.value);
3013                      activeThread = atoi(item.value);
3014                   }
3015                   else if(!strcmp(item.name, "value"))
3016                   {
3017                      StripQuotes(item.value, item.value);
3018                      eval.result = CopyString(item.value);
3019                      eval.active = false;
3020                   }
3021                   else if(!strcmp(item.name, "addr"))
3022                   {
3023                      for(i = 2; i < outTokens.count; i++)
3024                      {
3025                         if(TokenizeListItem(outTokens[i], item))
3026                         {
3027                            if(!strcmp(item.name, "total-bytes"))
3028                            {
3029                               StripQuotes(item.value, item.value);
3030                               eval.bytes = atoi(item.value);
3031                            }
3032                            else if(!strcmp(item.name, "next-row"))
3033                            {
3034                               StripQuotes(item.value, item.value);
3035                               eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3036                            }
3037                            else if(!strcmp(item.name, "memory"))
3038                            {
3039                               int j;
3040                               //int value;
3041                               //StripQuotes(item.value, item.value);
3042                               item.value = StripBrackets(item.value);
3043                               // this should be treated as a list...
3044                               item.value = StripCurlies(item.value);
3045                               TokenizeList(item.value, ',', subTokens);
3046                               for(j = 0; j < subTokens.count; j++)
3047                               {
3048                                  if(TokenizeListItem(subTokens[j], item))
3049                                  {
3050                                     if(!strcmp(item.name, "data"))
3051                                     {
3052                                        item.value = StripBrackets(item.value);
3053                                        StripQuotes2(item.value, item.value);
3054                                        eval.result = CopyString(item.value);
3055                                        eval.active = false;
3056                                     }
3057                                  }
3058                               }
3059                               subTokens.RemoveAll();
3060                            }
3061                         }
3062                      }
3063                   }
3064                   else if(!strcmp(item.name, "source-path"))
3065                   {
3066                   }
3067                   else
3068                      DebuggerProtocolUnknown("Unknown command reply", item.name);
3069                }
3070             }
3071             else if(!strcmp(outTokens[0], "^running"))
3072             {
3073                waitingForPID = true;
3074                setWaitingForPID = true;
3075             }
3076             else if(!strcmp(outTokens[0], "^exit"))
3077             {
3078                ChangeState(terminated);
3079                // ide.outputView.debugBox.Logf("Exit\n");
3080                // ide.Update(null);
3081                gdbReady = true;
3082                serialSemaphore.Release();
3083             }
3084             else if(!strcmp(outTokens[0], "^error"))
3085             {
3086                if(sentBreakInsert)
3087                {
3088                   sentBreakInsert = false;
3089                   breakpointError = true;
3090 #ifdef _DEBUG
3091                   if(bpItem)
3092                      printf("problem\n");
3093 #endif
3094                   bpItem = GdbDataBreakpoint { };
3095                }
3096
3097                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3098                {
3099                   if(!strcmp(item.name, "msg"))
3100                   {
3101                      StripQuotes(item.value, item.value);
3102                      if(eval.active)
3103                      {
3104                         eval.active = false;
3105                         eval.result = null;
3106                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3107                            eval.error = symbolNotFound;
3108                         else if(strstr(item.value, "Cannot access memory at address"))
3109                            eval.error = memoryCantBeRead;
3110                         else
3111                            eval.error = unknown;
3112                      }
3113                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3114                      {
3115                      }
3116                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3117                      {
3118                      }
3119                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3120                      {
3121                         ChangeState(stopped);
3122                         gdbHandle.Printf("-exec-continue\n");
3123                      }
3124                      else if(!strcmp(item.value, "ptrace: No such process."))
3125                      {
3126                         ChangeState(loaded);
3127                         targetProcessId = 0;
3128                      }
3129                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3130                      {
3131                      }
3132                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3133                      {
3134                         ChangeState(loaded);
3135                         targetProcessId = 0;
3136                      }
3137                      else if(strstr(item.value, "No such file or directory."))
3138                      {
3139                         ChangeState(loaded);
3140                         targetProcessId = 0;
3141                      }
3142                      else if(strstr(item.value, "During startup program exited with code "))
3143                      {
3144                         ChangeState(loaded);
3145                         targetProcessId = 0;
3146                      }
3147                      else
3148                      {
3149 #ifdef _DEBUG
3150                         if(strlen(item.value) < MAX_F_STRING)
3151                         {
3152                            char * s;
3153                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3154                            delete s;
3155                         }
3156                         else
3157                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3158 #endif
3159                      }
3160                   }
3161                }
3162                else
3163                   DebuggerProtocolUnknown("Unknown error content", item.name);
3164             }
3165             else
3166                DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3167             
3168             outTokens.RemoveAll();
3169             break;
3170          case '+':
3171             DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3172             break;
3173          case '=':
3174             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3175                ;
3176             else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3177                ;
3178             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"
3179                ;
3180             else
3181                DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3182             outTokens.RemoveAll();
3183             break;
3184          case '*':
3185             gdbReady = false;
3186             if(TokenizeList(output, ',', outTokens))
3187             {
3188                if(!strcmp(outTokens[0],"*running"))
3189                {
3190                   waitingForPID = true;
3191                   setWaitingForPID = true;
3192                }
3193                else if(!strcmp(outTokens[0], "*stopped"))
3194                {
3195                   int tk;
3196                   ChangeState(stopped);
3197
3198                   for(tk = 1; tk < outTokens.count; tk++)
3199                   {
3200                      if(TokenizeListItem(outTokens[tk], item))
3201                      {
3202                         if(!strcmp(item.name, "reason"))
3203                         {
3204                            char * reason = item.value;
3205                            StripQuotes(reason, reason);
3206                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3207                            {
3208                               char * exitCode;
3209                               if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3210                               {
3211                                  tk++;
3212                                  StripQuotes(item2.value, item2.value);
3213                                  if(!strcmp(item2.name, "exit-code"))
3214                                     exitCode = item2.value;
3215                                  else
3216                                     exitCode = null;
3217                               }
3218                               else
3219                                  exitCode = null;
3220                               HandleExit(reason, exitCode);
3221                            }
3222                            else if(!strcmp(reason, "breakpoint-hit"))
3223                            {
3224       #ifdef _DEBUG
3225                               if(stopItem)
3226                                  printf("problem\n");
3227       #endif
3228                               stopItem = GdbDataStop { };
3229
3230                               for(i = tk+1; i < outTokens.count; i++)
3231                               {
3232                                  TokenizeListItem(outTokens[i], item);
3233                                  StripQuotes(item.value, item.value);
3234                                  if(!strcmp(item.name, "bkptno"))
3235                                     stopItem.bkptno = atoi(item.value);
3236                                  else if(!strcmp(item.name, "thread-id"))
3237                                     stopItem.threadid = atoi(item.value);
3238                                  else if(!strcmp(item.name, "frame"))
3239                                  {
3240                                     item.value = StripCurlies(item.value);
3241                                     ParseFrame(stopItem.frame, item.value);
3242                                  }
3243                                  else
3244                                     DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3245                               }
3246
3247                               event = hit;
3248                            }
3249                            else if(!strcmp(reason, "end-stepping-range"))
3250                            {
3251       #ifdef _DEBUG
3252                               if(stopItem)
3253                                  printf("problem\n");
3254       #endif
3255                               stopItem = GdbDataStop { };
3256
3257                               for(i = tk+1; i < outTokens.count; i++)
3258                               {
3259                                  TokenizeListItem(outTokens[i], item);
3260                                  StripQuotes(item.value, item.value);
3261                                  if(!strcmp(item.name, "thread-id"))
3262                                     stopItem.threadid = atoi(item.value);
3263                                  else if(!strcmp(item.name, "frame"))
3264                                  {
3265                                     item.value = StripCurlies(item.value);
3266                                     ParseFrame(stopItem.frame, item.value);
3267                                  }
3268                                  else if(!strcmp(item.name, "reason"))
3269                                     ;
3270                                  else if(!strcmp(item.name, "bkptno"))
3271                                     ;
3272                                  else
3273                                     DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3274                               }
3275
3276                               event = stepEnd;
3277                               ide.Update(null);
3278                            }
3279                            else if(!strcmp(reason, "function-finished"))
3280                            {
3281       #ifdef _DEBUG
3282                               if(stopItem)
3283                                  printf("problem\n");
3284       #endif
3285                               stopItem = GdbDataStop { };
3286                               stopItem.reason = CopyString(reason);
3287
3288                               for(i = tk+1; i < outTokens.count; i++)
3289                               {
3290                                  TokenizeListItem(outTokens[i], item);
3291                                  StripQuotes(item.value, item.value);
3292                                  if(!strcmp(item.name, "thread-id"))
3293                                     stopItem.threadid = atoi(item.value);
3294                                  else if(!strcmp(item.name, "frame"))
3295                                  {
3296                                     item.value = StripCurlies(item.value);
3297                                     ParseFrame(stopItem.frame, item.value);
3298                                  }
3299                                  else if(!strcmp(item.name, "gdb-result-var"))
3300                                     stopItem.gdbResultVar = CopyString(item.value);
3301                                  else if(!strcmp(item.name, "return-value"))
3302                                     stopItem.returnValue = CopyString(item.value);
3303                                  else
3304                                     DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3305                               }
3306
3307                               event = functionEnd;
3308                               ide.Update(null);
3309                            }
3310                            else if(!strcmp(reason, "signal-received"))
3311                            {
3312       #ifdef _DEBUG
3313                               if(stopItem)
3314                                  printf("problem\n");
3315       #endif
3316                               stopItem = GdbDataStop { };
3317                               stopItem.reason = CopyString(reason);
3318
3319                               for(i = tk+1; i < outTokens.count; i++)
3320                               {
3321                                  TokenizeListItem(outTokens[i], item);
3322                                  StripQuotes(item.value, item.value);
3323                                  if(!strcmp(item.name, "signal-name"))
3324                                     stopItem.name = CopyString(item.value);
3325                                  else if(!strcmp(item.name, "signal-meaning"))
3326                                     stopItem.meaning = CopyString(item.value);
3327                                  else if(!strcmp(item.name, "thread-id"))
3328                                     stopItem.threadid = atoi(item.value);
3329                                  else if(!strcmp(item.name, "frame"))
3330                                  {
3331                                     item.value = StripCurlies(item.value);
3332                                     ParseFrame(stopItem.frame, item.value);
3333                                  }
3334                                  else
3335                                     DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3336                               }
3337                               if(!strcmp(stopItem.name, "SIGTRAP"))
3338                               {
3339                                  switch(breakType)
3340                                  {
3341                                     case internal:
3342                                        breakType = none;
3343                                        break;
3344                                     case restart:
3345                                     case stop:
3346                                        break;
3347                                     default:
3348                                        event = breakEvent;
3349                                  }
3350                               }
3351                               else
3352                               {
3353                                  event = signal;
3354                               }
3355                            }
3356                            else if(!strcmp(reason, "watchpoint-trigger"))
3357                               DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3358                            else if(!strcmp(reason, "read-watchpoint-trigger"))
3359                               DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3360                            else if(!strcmp(reason, "access-watchpoint-trigger"))
3361                               DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3362                            else if(!strcmp(reason, "watchpoint-scope"))
3363                               DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3364                            else if(!strcmp(reason, "location-reached"))
3365                               DebuggerProtocolUnknown("Reason location reached not handled", "");
3366                            else
3367                               DebuggerProtocolUnknown("Unknown reason", reason);
3368                         }
3369                      }
3370                   }
3371                   app.SignalEvent();
3372                }
3373             }
3374             else
3375                DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3376             outTokens.RemoveAll();
3377             break;
3378          case '(':
3379             if(!strcmpi(output, "(gdb) "))
3380             {
3381                if(waitingForPID)
3382                {
3383                   char exeFile[MAX_LOCATION];
3384                   int oldProcessID = targetProcessId;
3385                   GetLastDirectory(targetFile, exeFile);
3386
3387                   while(true)
3388                   {
3389                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3390                      if(targetProcessId || gdbHandle.Peek()) break;
3391                      Sleep(0.01);
3392                   }
3393
3394                   if(targetProcessId)
3395                      ChangeState(running);
3396                   else if(!oldProcessID)
3397                   {
3398                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3399                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3400                      gdbHandle.Printf("-gdb-exit\n");
3401                      gdbTimer.Stop();
3402                      ChangeState(terminated); //loaded;
3403                      prjConfig = null;
3404
3405                      if(ide.workspace)
3406                      {
3407                         for(bp : ide.workspace.breakpoints)
3408                            bp.inserted = false;
3409                      }
3410                      for(bp : sysBPs)
3411                         bp.inserted = false;
3412                      if(bpRunToCursor)
3413                         bpRunToCursor.inserted = false;
3414                      
3415                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3416                      ClearBreakDisplay();
3417
3418                #if defined(__unix__)
3419                      if(FileExists(progFifoPath)) //fileCreated)
3420                      {
3421                         progThread.terminate = true;
3422                         if(fifoFile)
3423                         {
3424                            fifoFile.CloseInput();
3425                            app.Unlock();
3426                            progThread.Wait();
3427                            app.Lock();
3428                            delete fifoFile;
3429                         }
3430
3431                         DeleteFile(progFifoPath);
3432                         progFifoPath[0] = '\0';
3433                         rmdir(progFifoDir);
3434                      }
3435                #endif
3436                   }
3437                }
3438                gdbReady = true;
3439                serialSemaphore.Release();
3440             }
3441             else
3442                DebuggerProtocolUnknown($"Unknown prompt", output);
3443
3444             break;
3445          case '&':
3446             if(!strncmp(output, "&\"warning:", 10))
3447             {
3448                char * content;
3449                content = strstr(output, "\"");
3450                StripQuotes(content, content);
3451                content = strstr(content, ":");
3452                if(content)
3453                   content++;
3454                if(content)
3455                {
3456                   char * s;
3457                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3458                   delete s;
3459                   ide.Update(null);
3460                }
3461             }
3462             break;
3463          default:
3464             DebuggerProtocolUnknown($"Unknown output", output);
3465       }
3466       if(!setWaitingForPID)
3467          waitingForPID = false;
3468       setWaitingForPID = false;
3469
3470       delete outTokens;
3471       delete subTokens;
3472       delete item;
3473       delete item2;
3474    }
3475
3476    void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3477    {
3478       if(bpRunToCursor)
3479       {
3480          //bpRunToCursor.Free();
3481          bpRunToCursor = Breakpoint { };
3482       }
3483       else
3484          bpRunToCursor = Breakpoint { };
3485
3486       if(absoluteFilePath)
3487          bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3488       if(relativeFilePath)
3489          bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3490       bpRunToCursor.line = lineNumber;
3491       bpRunToCursor.type = runToCursor;
3492       bpRunToCursor.enabled = true;
3493       bpRunToCursor.condition = null;
3494       bpRunToCursor.ignore = 0;
3495       bpRunToCursor.level = -1;
3496    }
3497
3498    ExpressionType ::DebugEvalExpTypeError(char * result)
3499    {
3500       if(result)
3501          return dummyExp;
3502       switch(eval.error)
3503       {
3504          case symbolNotFound:
3505             return symbolErrorExp;
3506          case memoryCantBeRead:
3507             return memoryErrorExp;
3508       }
3509       return unknownErrorExp;
3510    }
3511
3512    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3513    {
3514       char * result;
3515       if(ide.projectView && ide.debugger.state == stopped)
3516       {
3517          result = GdbEvaluateExpression(expression);
3518          *error = DebugEvalExpTypeError(result);
3519       }
3520       else
3521       {
3522          result = null;
3523          *error = noDebuggerErrorExp;
3524       }
3525       return result;
3526    }
3527
3528    char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3529    {
3530       // check for state
3531       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3532       if(!result || !strcmp(result, "N/A"))
3533          *error = memoryErrorExp;
3534       else
3535          *error = DebugEvalExpTypeError(result);
3536       return result;
3537    }
3538 }
3539
3540 class GdbThread : Thread
3541 {
3542    Debugger debugger;
3543
3544    unsigned int Main()
3545    {
3546       static char output[4096];
3547       Array<char> dynamicBuffer { minAllocSize = 4096 };
3548       DualPipe oldGdbHandle = gdbHandle;
3549       incref oldGdbHandle;
3550
3551       app.Lock();
3552       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3553       {
3554          int result;
3555          app.Unlock();
3556          result = gdbHandle.Read(output, 1, sizeof(output));
3557          app.Lock();
3558          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3559             break;
3560          if(result)
3561          {
3562             int c;
3563             int start = 0;
3564
3565             for(c = 0; c<result; c++)
3566             {
3567                if(output[c] == '\n')
3568                {
3569                   int pos = dynamicBuffer.size;
3570                   dynamicBuffer.size += c - start;
3571                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3572                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3573                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3574                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3575                      dynamicBuffer.size++;
3576                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3577 #ifdef _DEBUG
3578                   // printf("%s\n", dynamicBuffer.array);
3579 #endif
3580                   debugger.GdbThreadMain(&dynamicBuffer[0]);
3581                   dynamicBuffer.size = 0;
3582                   start = c + 1;
3583                }
3584             }
3585             if(c == result)
3586             {
3587                int pos = dynamicBuffer.size;
3588                dynamicBuffer.size += c - start;
3589                memcpy(&dynamicBuffer[pos], output + start, c - start);
3590             }
3591          }
3592          else
3593          {
3594 #ifdef _DEBUG
3595             printf("Got end of file from GDB!\n");
3596 #endif
3597          }
3598       }
3599       delete dynamicBuffer;
3600       //if(oldGdbHandle == gdbHandle)
3601          debugger.GdbThreadExit();
3602       delete oldGdbHandle;
3603       app.Unlock();
3604       return 0;
3605    }
3606 }
3607
3608 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3609 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3610
3611 #if defined(__unix__)
3612 #define uint _uint
3613 #include <errno.h>
3614 #include <stdio.h>
3615 #include <fcntl.h>
3616 #include <sys/types.h>
3617 #undef uint
3618
3619 File fifoFile;
3620
3621 class ProgramThread : Thread
3622 {
3623    bool terminate;
3624    unsigned int Main()
3625    {
3626       bool result = true;
3627       bool fileCreated = false;
3628       mode_t mask = 0600;
3629       static char output[1000];
3630       int fd;
3631
3632       /*if(!mkfifo(progFifoPath, mask))
3633       {
3634          fileCreated = true;
3635       }
3636       else
3637       {
3638          app.Lock();
3639          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3640          app.Unlock();
3641       }*/
3642
3643       if(FileExists(progFifoPath)) //fileCreated)
3644       {
3645          fifoFile = FileOpen(progFifoPath, read);
3646          if(!fifoFile)
3647          {
3648             app.Lock();
3649             ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3650             app.Unlock();
3651          }
3652          else
3653          {
3654             fd = fileno((FILE *)fifoFile.input);
3655             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3656          }
3657       }
3658
3659       while(!terminate && fifoFile && !fifoFile.Eof())
3660       {
3661          fd_set rs, es;
3662          struct timeval time;
3663          int selectResult;
3664          time.tv_sec = 1;
3665          time.tv_usec = 0;
3666          FD_ZERO(&rs);
3667          FD_ZERO(&es);
3668          FD_SET(fd, &rs);
3669          FD_SET(fd, &es);
3670          selectResult = select(fd + 1, &rs, null, null, &time);
3671          if(FD_ISSET(fd, &rs))
3672          {
3673             int result = read(fd, output, sizeof(output)-1);
3674             if(!result || (result < 0 && errno != EAGAIN))
3675                break;
3676             if(result > 0)
3677             {
3678                output[result] = '\0';
3679                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3680                {
3681                   app.Lock();
3682                   ide.outputView.debugBox.Log(output);
3683                   app.Unlock();
3684                }
3685             }
3686          }
3687       }
3688
3689       //if(fifoFile)
3690       {
3691          //fifoFile.CloseInput();
3692          //delete fifoFile;
3693          app.Lock();
3694          ide.outputView.debugBox.Log("\n");
3695          app.Unlock();
3696       }
3697       /*
3698       if(FileExists(progFifoPath)) //fileCreated)
3699       {
3700          DeleteFile(progFifoPath);
3701          progFifoPath[0] = '\0';
3702       }
3703       */
3704       return 0;
3705    }
3706 }
3707 #endif
3708
3709 class Argument : struct
3710 {
3711    Argument prev, next;
3712    char * name;
3713    char * value;
3714
3715    void Free()
3716    {
3717       delete name;
3718       delete value;
3719    }
3720
3721    ~Argument()
3722    {
3723       Free();
3724    }
3725 }
3726
3727 class Frame : struct
3728 {
3729    Frame prev, next;
3730    int level;
3731    char * addr;
3732    char * func;
3733    int argsCount;
3734    OldList args;
3735    char * from;
3736    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3737    char * file;
3738    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3739    char * absoluteFile;
3740    int line;
3741
3742    void Free()
3743    {
3744       delete addr;
3745       delete func;
3746       delete from;
3747       delete file;
3748       delete absoluteFile;
3749       args.Free(Argument::Free);
3750    }
3751
3752    ~Frame()
3753    {
3754       Free();
3755    }
3756 }
3757
3758 class GdbDataStop : struct
3759 {
3760    char * reason;
3761    int threadid;
3762    union
3763    {
3764       struct
3765       {
3766          int bkptno;
3767       };
3768       struct
3769       {
3770          char * name;
3771          char * meaning;
3772       };
3773       struct
3774       {
3775          char * gdbResultVar;
3776          char * returnValue;
3777       };
3778    };
3779    Frame frame { };
3780
3781    void Free()
3782    {
3783       if(reason)
3784       {
3785          if(!strcmp(reason, "signal-received"))
3786          {
3787             delete name;
3788             delete meaning;
3789          }
3790          else if(!strcmp(reason, "function-finished"))
3791          {
3792             delete gdbResultVar;
3793             delete returnValue;
3794          }
3795          delete reason;
3796       }
3797       if(frame) frame.Free();
3798    }
3799
3800    ~GdbDataStop()
3801    {
3802       Free();
3803    }
3804 }
3805
3806 class GdbDataBreakpoint : struct
3807 {
3808    int number;
3809    char * type;
3810    char * disp;
3811    bool enabled;
3812    char * addr;
3813    char * func;
3814    char * file;
3815    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3816    int line;
3817    char * at;
3818    int times;
3819
3820    void Free()
3821    {
3822       delete type;
3823       delete disp;
3824       delete addr;
3825       delete func;
3826       delete file;
3827       delete at;
3828    }
3829
3830    ~GdbDataBreakpoint()
3831    {
3832       Free();
3833    }
3834 }
3835
3836 class Breakpoint : struct
3837 {
3838    class_no_expansion;
3839
3840    char * relativeFilePath;
3841    char * absoluteFilePath;
3842    int line;
3843    bool enabled;
3844    int hits;
3845    int breaks;
3846    int ignore;
3847    int level;
3848    Watch condition;
3849    bool inserted;
3850    BreakpointType type;
3851    DataRow row;
3852    
3853    GdbDataBreakpoint bp;
3854
3855    char * LocationToString()
3856    {
3857       char location[MAX_LOCATION+20];
3858       snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3859       location[sizeof(location)-1] = 0;
3860 #if defined(__WIN32__)
3861       ChangeCh(location, '/', '\\');
3862 #endif
3863       return CopyString(location);
3864    }
3865
3866    void Save(File f)
3867    {
3868       if(relativeFilePath && relativeFilePath[0])
3869       {
3870          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3871          if(condition)
3872             f.Printf("       ~ %s\n", condition.expression);
3873       }
3874    }
3875
3876    void Free()
3877    {
3878       if(bp)
3879          bp.Free();
3880       delete bp;
3881       delete relativeFilePath;
3882       delete absoluteFilePath;
3883    }
3884
3885    ~Breakpoint()
3886    {
3887       Free();
3888    }
3889
3890 }
3891
3892 class Watch : struct
3893 {
3894    class_no_expansion;
3895    
3896    Type type;
3897    char * expression;
3898    char * value;
3899    DataRow row;
3900
3901    void Save(File f)
3902    {
3903       f.Printf("    ~ %s\n", expression);
3904    }
3905
3906    void Free()
3907    {
3908       delete expression;
3909       delete value;
3910       FreeType(type);
3911       type = null;
3912    }
3913
3914    void Reset()
3915    {
3916       delete value;
3917       FreeType(type);
3918       type = null;
3919    }
3920
3921    ~Watch()
3922    {
3923       Free();
3924    }
3925 }
3926
3927 class DebugListItem : struct
3928 {
3929    char * name;
3930    char * value;
3931 }
3932
3933 struct DebugEvaluationData
3934 {
3935    bool active;
3936    char * result;
3937    int bytes;
3938    uint64 nextBlockAddress;
3939
3940    DebuggerEvaluationError error;
3941 };
3942
3943 class CodeLocation : struct
3944 {
3945    char * file;
3946    char * absoluteFile;
3947    int line;
3948
3949    CodeLocation ::ParseCodeLocation(char * location)
3950    {
3951       if(location)
3952       {
3953          char * colon = null;
3954          char * temp;
3955          char loc[MAX_LOCATION];
3956          strcpy(loc, location);
3957          for(temp = loc; temp = strstr(temp, ":"); temp++)
3958             colon = temp;
3959          if(colon)
3960          {
3961             colon[0] = '\0';
3962             colon++;
3963             if(colon)
3964             {
3965                int line = atoi(colon);
3966                if(line)
3967                {
3968                   CodeLocation codloc { line = line };
3969                   codloc.file = CopyString(loc);
3970                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3971                   return codloc;
3972                }
3973             }
3974          }
3975       }
3976       return null;
3977    }
3978
3979    void Free()
3980    {
3981       delete file;
3982       delete absoluteFile;
3983    }
3984
3985    ~CodeLocation()
3986    {
3987       Free();
3988    }
3989 }