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