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