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