d99718688210c7a6be0305d150b4ad9a9911f0eb
[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                   ide.breakpointsView.UpdateBreakpoint(bp.row);
2633                   if(bp.hits > bp.ignore)
2634                   {
2635                      ignoreBreakpoints = false;
2636                      // Why was SelectFrame missing here?
2637                      SelectFrame(activeFrameLevel);
2638                      GoToStackFrameLine(activeFrameLevel, true);
2639                      //ide.AdjustDebugMenus();
2640                      ide.Activate();
2641                      ide.Update(null);
2642                      if(bp.type == BreakpointType::runToCursor)
2643                      {
2644                         delete bpRunToCursor;
2645                         bpRunToCursor = null;
2646                      }
2647                   }
2648                   else
2649                   {
2650                      /*bp.ignore--;
2651                      ide.breakpointsView.UpdateBreakpoint(bp.row);*/
2652                      GdbExecContinue(false);
2653                   }
2654                }
2655                else
2656                   GdbExecContinue(false);
2657                break;
2658          }
2659       }
2660       else
2661          ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2662    }
2663
2664    void GdbThreadExit()
2665    {
2666       if(state != terminated)
2667       {
2668          ChangeState(terminated);
2669          targetProcessId = 0;
2670          ClearBreakDisplay();
2671          //ide.AdjustDebugMenus();
2672
2673          if(gdbHandle)
2674          {
2675             serialSemaphore.Release();
2676             gdbTimer.Stop();
2677             gdbHandle.Wait();
2678             delete gdbHandle;
2679             
2680             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2681             ide.outputView.debugBox.Logf($"Debugging stopped\n");
2682             ide.Update(null);
2683          }
2684          //ChangeState(terminated);
2685       }
2686    }
2687
2688    void GdbThreadMain(char * output)
2689    {
2690       int i;
2691       Array<char *> outTokens { minAllocSize = 50 };
2692       Array<char *> subTokens { minAllocSize = 50 };
2693       DebugListItem item { };
2694       DebugListItem item2 { };
2695       bool setWaitingForPID = false;
2696       
2697 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2698 #ifdef GDB_DEBUG_CONSOLE
2699       Log(output); Log("\n");
2700 #endif
2701 #ifdef GDB_DEBUG_OUTPUT
2702       {
2703          int len = strlen(output);
2704          if(len > 1024)
2705          {
2706             int c;
2707             char * start;
2708             char tmp[1025];
2709             tmp[1024] = '\0';
2710             start = output;
2711             for(c = 0; c < len / 1024; c++)
2712             {
2713                strncpy(tmp, start, 1024);
2714                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2715                start += 1024;
2716             }
2717             ide.outputView.gdbBox.Logf("out: %s\n", start);
2718          }
2719          else
2720          {
2721             ide.outputView.gdbBox.Logf("out: %s\n", output);
2722          }
2723       }
2724 #endif
2725 #ifdef GDB_DEBUG_CONSOLE
2726          strcpy(lastGdbOutput, output);
2727 #endif
2728 #ifdef GDB_DEBUG_GUI
2729          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2730 #endif
2731 #endif
2732       
2733       switch(output[0])
2734       {
2735          case '~':
2736             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2737             {
2738                symbols = false;
2739                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2740                ide.Update(null);
2741             }
2742             break;
2743          case '^':
2744             gdbReady = false;
2745             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2746             {
2747                //if(outTokens.count == 1)
2748                {
2749                   if(sentKill)
2750                   {
2751                      sentKill = false;
2752                      ChangeState(loaded);
2753                      targetProcessId = 0;
2754                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2755                      {
2756                         if(!strcmp(item.name, "reason"))
2757                         {
2758                            char * reason = item.value;
2759                            StripQuotes(reason, reason);
2760                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2761                            {
2762                               char * exitCode;
2763                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2764                               {
2765                                  StripQuotes(item2.value, item2.value);
2766                                  if(!strcmp(item2.name, "exit-code"))
2767                                     exitCode = item2.value;
2768                                  else
2769                                     exitCode = null;
2770                               }
2771                               else
2772                                  exitCode = null;
2773                               HandleExit(reason, exitCode);
2774                            }
2775                         }
2776                         else
2777                            DebuggerProtocolUnknown("Unknown kill reply", item.name);
2778                      }
2779                      else
2780                         HandleExit(null, null);
2781                   }
2782                }
2783                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2784                {
2785                   if(!strcmp(item.name, "bkpt"))
2786                   {
2787                      sentBreakInsert = false;
2788 #ifdef _DEBUG
2789                      if(bpItem)
2790                         printf("problem\n");
2791 #endif
2792                      bpItem = GdbDataBreakpoint { };
2793                      item.value = StripCurlies(item.value);
2794                      TokenizeList(item.value, ',', subTokens);
2795                      for(i = 0; i < subTokens.count; i++)
2796                      {
2797                         if(TokenizeListItem(subTokens[i], item))
2798                         {
2799                            StripQuotes(item.value, item.value);
2800                            if(!strcmp(item.name, "number"))
2801                               bpItem.number = atoi(item.value);
2802                            else if(!strcmp(item.name, "type"))
2803                               bpItem.type = CopyString(item.value);
2804                            else if(!strcmp(item.name, "disp"))
2805                               bpItem.disp = CopyString(item.value);
2806                            else if(!strcmp(item.name, "enabled"))
2807                               bpItem.enabled = (!strcmpi(item.value, "y"));
2808                            else if(!strcmp(item.name, "addr"))
2809                               bpItem.addr = CopyString(item.value);
2810                            else if(!strcmp(item.name, "func"))
2811                               bpItem.func = CopyString(item.value);
2812                            else if(!strcmp(item.name, "file"))
2813                               bpItem.file = item.value;
2814                            else if(!strcmp(item.name, "line"))
2815                               bpItem.line = atoi(item.value);
2816                            else if(!strcmp(item.name, "at"))
2817                               bpItem.at = CopyString(item.value);
2818                            else if(!strcmp(item.name, "times"))
2819                               bpItem.times = atoi(item.value);
2820                         }
2821                      }
2822                      //breakType = bpValidation;
2823                      //app.SignalEvent();
2824                      subTokens.RemoveAll();
2825                   }
2826                   else if(!strcmp(item.name, "BreakpointTable"))
2827                      ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2828                   else if(!strcmp(item.name, "depth"))
2829                   {
2830                      StripQuotes(item.value, item.value);
2831                      frameCount = atoi(item.value);
2832                      activeFrame = null;
2833                      stackFrames.Free(Frame::Free);
2834                   }
2835                   else if(!strcmp(item.name, "stack"))
2836                   {
2837                      Frame frame;
2838                      if(stackFrames.count)
2839                         ide.callStackView.Logf("...\n");
2840                      else
2841                         activeFrame = null;
2842                      item.value = StripBrackets(item.value);
2843                      TokenizeList(item.value, ',', subTokens);
2844                      for(i = 0; i < subTokens.count; i++)
2845                      {
2846                         if(TokenizeListItem(subTokens[i], item))
2847                         {
2848                            if(!strcmp(item.name, "frame"))
2849                            {
2850                               frame = Frame { };
2851                               stackFrames.Add(frame);
2852                               item.value = StripCurlies(item.value);
2853                               ParseFrame(frame, item.value);
2854                               if(frame.file && frame.from)
2855                                  DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2856                               if(frame.file)
2857                               {
2858                                  char * s;
2859                                  if(activeFrameLevel == -1)
2860                                  {
2861                                     if(ide.projectView.IsModuleInProject(frame.file));
2862                                     {
2863                                        if(frame.level != 0)
2864                                        {
2865                                           //stopItem.frame = frame;
2866                                           breakType = selectFrame;
2867                                        }
2868                                        else
2869                                           activeFrame = frame;
2870                                        activeFrameLevel = frame.level;
2871                                     }
2872                                  }
2873                                  ide.callStackView.Logf("%3d ", frame.level);
2874                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2875                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2876                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2877                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2878                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2879                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2880                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2881                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2882                                  else
2883                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2884                                  delete s;
2885                               }
2886                               else
2887                               {
2888                                  ide.callStackView.Logf("%3d ", frame.level);
2889
2890                                  if(frame.from)
2891                                  {
2892                                     char * s;
2893                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2894                                     delete s;
2895                                  }
2896                                  else if(frame.func)
2897                                     ide.callStackView.Logf("%s\n", frame.func);
2898                                  else
2899                                     ide.callStackView.Logf($"unknown source\n");
2900                               }
2901                            }
2902                            else
2903                               DebuggerProtocolUnknown("Unknown stack content", item.name);
2904                         }
2905                      }
2906                      if(activeFrameLevel == -1)
2907                      {
2908                         activeFrameLevel = 0;
2909                         activeFrame = stackFrames.first;
2910                      }
2911                      ide.callStackView.Home();
2912                      ide.Update(null);
2913                      subTokens.RemoveAll();
2914                   }
2915                   /*else if(!strcmp(item.name, "frame"))
2916                   {
2917                      Frame frame { };
2918                      item.value = StripCurlies(item.value);
2919                      ParseFrame(&frame, item.value);
2920                   }*/
2921                   else if(!strcmp(item.name, "thread-ids"))
2922                   {
2923                      ide.threadsView.Clear();
2924                      item.value = StripCurlies(item.value);
2925                      TokenizeList(item.value, ',', subTokens);
2926                      for(i = subTokens.count - 1; ; i--)
2927                      {
2928                         if(TokenizeListItem(subTokens[i], item))
2929                         {
2930                            if(!strcmp(item.name, "thread-id"))
2931                            {
2932                               int value;
2933                               StripQuotes(item.value, item.value);
2934                               value = atoi(item.value);
2935                               ide.threadsView.Logf("%3d \n", value);
2936                            }
2937                            else
2938                               DebuggerProtocolUnknown("Unknown threads content", item.name);
2939                         }
2940                         if(!i)
2941                            break;
2942                      }
2943                      ide.threadsView.Home();
2944                      ide.Update(null);
2945                      subTokens.RemoveAll();
2946                      //if(!strcmp(outTokens[2], "number-of-threads"))
2947                   }
2948                   else if(!strcmp(item.name, "new-thread-id"))
2949                   {
2950                      StripQuotes(item.value, item.value);
2951                      activeThread = atoi(item.value);
2952                   }
2953                   else if(!strcmp(item.name, "value"))
2954                   {
2955                      StripQuotes(item.value, item.value);
2956                      eval.result = CopyString(item.value);
2957                      eval.active = false;
2958                   }
2959                   else if(!strcmp(item.name, "addr"))
2960                   {
2961                      for(i = 2; i < outTokens.count; i++)
2962                      {
2963                         if(TokenizeListItem(outTokens[i], item))
2964                         {
2965                            if(!strcmp(item.name, "total-bytes"))
2966                            {
2967                               StripQuotes(item.value, item.value);
2968                               eval.bytes = atoi(item.value);
2969                            }
2970                            else if(!strcmp(item.name, "next-row"))
2971                            {
2972                               StripQuotes(item.value, item.value);
2973                               eval.nextBlockAddress = strtoul(item.value, null, 0);
2974                            }
2975                            else if(!strcmp(item.name, "memory"))
2976                            {
2977                               int j;
2978                               //int value;
2979                               //StripQuotes(item.value, item.value);
2980                               item.value = StripBrackets(item.value);
2981                               // this should be treated as a list...
2982                               item.value = StripCurlies(item.value);
2983                               TokenizeList(item.value, ',', subTokens);
2984                               for(j = 0; j < subTokens.count; j++)
2985                               {
2986                                  if(TokenizeListItem(subTokens[j], item))
2987                                  {
2988                                     if(!strcmp(item.name, "data"))
2989                                     {
2990                                        item.value = StripBrackets(item.value);
2991                                        StripQuotes2(item.value, item.value);
2992                                        eval.result = CopyString(item.value);
2993                                        eval.active = false;
2994                                     }
2995                                  }
2996                               }
2997                               subTokens.RemoveAll();
2998                            }
2999                         }
3000                      }
3001                   }
3002                   else if(!strcmp(item.name, "source-path"))
3003                   {
3004                   }
3005                   else
3006                      DebuggerProtocolUnknown("Unknown command reply", item.name);
3007                }
3008             }
3009             else if(!strcmp(outTokens[0], "^running"))
3010             {
3011                waitingForPID = true;
3012                setWaitingForPID = true;
3013             }
3014             else if(!strcmp(outTokens[0], "^exit"))
3015             {
3016                ChangeState(terminated);
3017                //ide.AdjustDebugMenus();
3018                // ide.outputView.debugBox.Logf("Exit\n");
3019                //ide.Update(null);
3020                gdbReady = true;
3021                serialSemaphore.Release();
3022             }
3023             else if(!strcmp(outTokens[0], "^error"))
3024             {
3025                if(sentBreakInsert)
3026                {
3027                   sentBreakInsert = false;
3028 #ifdef _DEBUG
3029                   if(bpItem)
3030                      printf("problem\n");
3031 #endif
3032                   bpItem = GdbDataBreakpoint { };
3033                }
3034
3035                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3036                {
3037                   if(!strcmp(item.name, "msg"))
3038                   {
3039                      StripQuotes(item.value, item.value);
3040                      if(eval.active)
3041                      {
3042                         eval.active = false;
3043                         eval.result = null;
3044                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3045                            eval.error = symbolNotFound;
3046                         else if(strstr(item.value, "Cannot access memory at address"))
3047                            eval.error = memoryCantBeRead;
3048                         else
3049                            eval.error = unknown;
3050                      }
3051                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3052                      {
3053                      }
3054                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3055                      {
3056                      }
3057                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3058                      {
3059                         ChangeState(stopped);
3060                         gdbHandle.Printf("-exec-continue\n");
3061                      }
3062                      else if(!strcmp(item.value, "ptrace: No such process."))
3063                      {
3064                         ChangeState(loaded);
3065                         targetProcessId = 0;
3066                         //ide.AdjustDebugMenus();
3067                      }
3068                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3069                      {
3070                      }
3071                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3072                      {
3073                         ChangeState(loaded);
3074                         targetProcessId = 0;
3075                         //ide.AdjustDebugMenus();
3076                      }
3077                      else if(strstr(item.value, "No such file or directory."))
3078                      {
3079                         ChangeState(loaded);
3080                         targetProcessId = 0;
3081                         //ide.AdjustDebugMenus();
3082                      }
3083                      else if(strstr(item.value, "During startup program exited with code "))
3084                      {
3085                         ChangeState(loaded);
3086                         targetProcessId = 0;
3087                         //ide.AdjustDebugMenus();
3088                      }
3089                      else
3090                      {
3091 #ifdef _DEBUG
3092                         if(strlen(item.value) < MAX_F_STRING)
3093                         {
3094                            char * s;
3095                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3096                            delete s;
3097                         }
3098                         else
3099                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3100 #endif
3101                      }
3102                   }
3103                }
3104                else
3105                   DebuggerProtocolUnknown("Unknown error content", item.name);
3106             }
3107             else
3108                DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3109             
3110             outTokens.RemoveAll();
3111             break;
3112          case '+':
3113             DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3114             break;
3115          case '=':
3116             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3117                ;
3118             else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3119                ;
3120             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"
3121                ;
3122             else
3123                DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3124             outTokens.RemoveAll();
3125             break;
3126          case '*':
3127             gdbReady = false;
3128             if(TokenizeList(output, ',', outTokens))
3129             {
3130                if(!strcmp(outTokens[0],"*running"))
3131                {
3132                   waitingForPID = true;
3133                   setWaitingForPID = true;
3134                }
3135                else if(!strcmp(outTokens[0], "*stopped"))
3136                {
3137                   ChangeState(stopped);
3138                   
3139                   if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3140                   {
3141                      if(!strcmp(item.name, "reason"))
3142                      {
3143                         char * reason = item.value;
3144                         StripQuotes(reason, reason);
3145                         if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3146                         {
3147                            char * exitCode;
3148                            if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3149                            {
3150                               StripQuotes(item2.value, item2.value);
3151                               if(!strcmp(item2.name, "exit-code"))
3152                                  exitCode = item2.value;
3153                               else
3154                                  exitCode = null;
3155                            }
3156                            else
3157                               exitCode = null;
3158                            HandleExit(reason, exitCode);
3159                         }
3160                         else if(!strcmp(reason, "breakpoint-hit"))
3161                         {
3162    #ifdef _DEBUG
3163                            if(stopItem)
3164                               printf("problem\n");
3165    #endif
3166                            stopItem = GdbDataStop { };
3167
3168                            for(i = 2; i < outTokens.count; i++)
3169                            {
3170                               TokenizeListItem(outTokens[i], item);
3171                               StripQuotes(item.value, item.value);
3172                               if(!strcmp(item.name, "bkptno"))
3173                                  stopItem.bkptno = atoi(item.value);
3174                               else if(!strcmp(item.name, "thread-id"))
3175                                  stopItem.threadid = atoi(item.value);
3176                               else if(!strcmp(item.name, "frame"))
3177                               {
3178                                  item.value = StripCurlies(item.value);
3179                                  ParseFrame(stopItem.frame, item.value);
3180                               }
3181                               else
3182                                  DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3183                            }
3184
3185                            event = hit;
3186                         }
3187                         else if(!strcmp(reason, "end-stepping-range"))
3188                         {
3189    #ifdef _DEBUG
3190                            if(stopItem)
3191                               printf("problem\n");
3192    #endif
3193                            stopItem = GdbDataStop { };
3194
3195                            for(i = 2; i < outTokens.count; i++)
3196                            {
3197                               TokenizeListItem(outTokens[i], item);
3198                               StripQuotes(item.value, item.value);
3199                               if(!strcmp(item.name, "thread-id"))
3200                                  stopItem.threadid = atoi(item.value);
3201                               else if(!strcmp(item.name, "frame"))
3202                               {
3203                                  item.value = StripCurlies(item.value);
3204                                  ParseFrame(stopItem.frame, item.value);
3205                               }
3206                               else if(!strcmp(item.name, "reason"))
3207                                  ;
3208                               else if(!strcmp(item.name, "bkptno"))
3209                                  ;
3210                               else
3211                                  DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3212                            }
3213
3214                            event = stepEnd;
3215                            //ide.AdjustDebugMenus();
3216                            ide.Update(null);
3217                         }
3218                         else if(!strcmp(reason, "function-finished"))
3219                         {
3220    #ifdef _DEBUG
3221                            if(stopItem)
3222                               printf("problem\n");
3223    #endif
3224                            stopItem = GdbDataStop { };
3225                            stopItem.reason = CopyString(reason);
3226
3227                            for(i = 2; i < outTokens.count; i++)
3228                            {
3229                               TokenizeListItem(outTokens[i], item);
3230                               StripQuotes(item.value, item.value);
3231                               if(!strcmp(item.name, "thread-id"))
3232                                  stopItem.threadid = atoi(item.value);
3233                               else if(!strcmp(item.name, "frame"))
3234                               {
3235                                  item.value = StripCurlies(item.value);
3236                                  ParseFrame(stopItem.frame, item.value);
3237                               }
3238                               else if(!strcmp(item.name, "gdb-result-var"))
3239                                  stopItem.gdbResultVar = CopyString(item.value);
3240                               else if(!strcmp(item.name, "return-value"))
3241                                  stopItem.returnValue = CopyString(item.value);
3242                               else
3243                                  DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3244                            }
3245
3246                            event = functionEnd;
3247                            //ide.AdjustDebugMenus();
3248                            ide.Update(null);
3249                         }
3250                         else if(!strcmp(reason, "signal-received"))
3251                         {
3252    #ifdef _DEBUG
3253                            if(stopItem)
3254                               printf("problem\n");
3255    #endif
3256                            stopItem = GdbDataStop { };
3257                            stopItem.reason = CopyString(reason);
3258
3259                            for(i = 2; i < outTokens.count; i++)
3260                            {
3261                               TokenizeListItem(outTokens[i], item);
3262                               StripQuotes(item.value, item.value);
3263                               if(!strcmp(item.name, "signal-name"))
3264                                  stopItem.name = CopyString(item.value);
3265                               else if(!strcmp(item.name, "signal-meaning"))
3266                                  stopItem.meaning = CopyString(item.value);
3267                               else if(!strcmp(item.name, "thread-id"))
3268                                  stopItem.threadid = atoi(item.value);
3269                               else if(!strcmp(item.name, "frame"))
3270                               {
3271                                  item.value = StripCurlies(item.value);
3272                                  ParseFrame(stopItem.frame, item.value);
3273                               }
3274                               else
3275                                  DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3276                            }
3277                            if(!strcmp(stopItem.name, "SIGTRAP"))
3278                            {
3279                               switch(breakType)
3280                               {
3281                                  case internal:
3282                                     breakType = none;
3283                                     break;
3284                                  case restart:
3285                                  case stop:
3286                                     break;
3287                                  default:
3288                                     event = breakEvent;
3289                                     //ide.AdjustDebugMenus(); ide.Update(null);
3290                               }
3291                            }
3292                            else
3293                            {
3294                               event = signal;
3295                               //ide.AdjustDebugMenus(); ide.Update(null);
3296                            }
3297                         }
3298                         else if(!strcmp(reason, "watchpoint-trigger"))
3299                            DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3300                         else if(!strcmp(reason, "read-watchpoint-trigger"))
3301                            DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3302                         else if(!strcmp(reason, "access-watchpoint-trigger"))
3303                            DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3304                         else if(!strcmp(reason, "watchpoint-scope"))
3305                            DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3306                         else if(!strcmp(reason, "location-reached"))
3307                            DebuggerProtocolUnknown("Reason location reached not handled", "");
3308                         else
3309                            DebuggerProtocolUnknown("Unknown reason", reason);
3310                      }
3311                   }
3312                   app.SignalEvent();
3313                }
3314             }
3315             else
3316                DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3317             outTokens.RemoveAll();
3318             break;
3319          case '(':
3320             if(!strcmpi(output, "(gdb) "))
3321             {
3322                if(waitingForPID)
3323                {
3324                   char exeFile[MAX_LOCATION];
3325                   int oldProcessID = targetProcessId;
3326                   GetLastDirectory(targetFile, exeFile);
3327
3328                   while(true)
3329                   {
3330                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3331                      if(targetProcessId || gdbHandle.Peek()) break;
3332                      Sleep(0.01);
3333                   }
3334
3335                   if(targetProcessId)
3336                   {
3337                      ChangeState(running);
3338                      //ide.AdjustDebugMenus(); ide.Update(null);
3339                   }
3340                   else if(!oldProcessID)
3341                   {
3342                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3343                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3344                      gdbHandle.Printf("-gdb-exit\n");
3345                      gdbTimer.Stop();
3346                      ChangeState(terminated); //loaded;
3347                      prjConfig = null;
3348
3349                      if(ide.workspace)
3350                      {
3351                         for(bp : ide.workspace.breakpoints)
3352                            bp.inserted = false;
3353                      }
3354                      for(bp : sysBPs)
3355                         bp.inserted = false;
3356                      if(bpRunToCursor)
3357                         bpRunToCursor.inserted = false;
3358                      
3359                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3360                      ClearBreakDisplay();
3361                      //ide.AdjustDebugMenus(); ide.Update(null);
3362
3363                #if defined(__unix__)
3364                      if(FileExists(progFifoPath)) //fileCreated)
3365                      {
3366                         progThread.terminate = true;
3367                         if(fifoFile)
3368                         {
3369                            fifoFile.CloseInput();
3370                            app.Unlock();
3371                            progThread.Wait();
3372                            app.Lock();
3373                            delete fifoFile;
3374                         }
3375
3376                         DeleteFile(progFifoPath);
3377                         progFifoPath[0] = '\0';
3378                         rmdir(progFifoDir);
3379                      }
3380                #endif
3381                   }
3382                }
3383                gdbReady = true;
3384                serialSemaphore.Release();
3385             }
3386             else
3387                DebuggerProtocolUnknown($"Unknown prompt", output);
3388
3389             break;
3390          case '&':
3391             if(!strncmp(output, "&\"warning:", 10))
3392             {
3393                char * content;
3394                content = strstr(output, "\"");
3395                StripQuotes(content, content);
3396                content = strstr(content, ":");
3397                if(content)
3398                   content++;
3399                if(content)
3400                {
3401                   char * s;
3402                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3403                   delete s;
3404                   ide.Update(null);
3405                }
3406             }
3407             break;
3408          default:
3409             DebuggerProtocolUnknown($"Unknown output", output);
3410       }
3411       if(!setWaitingForPID)
3412          waitingForPID = false;
3413       setWaitingForPID = false;
3414
3415       delete outTokens;
3416       delete subTokens;
3417       delete item;
3418       delete item2;
3419    }
3420
3421    void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3422    {
3423       if(bpRunToCursor)
3424       {
3425          //bpRunToCursor.Free();
3426          bpRunToCursor = Breakpoint { };
3427       }
3428       else
3429          bpRunToCursor = Breakpoint { };
3430
3431       if(absoluteFilePath)
3432          bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3433       if(relativeFilePath)
3434          bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3435       bpRunToCursor.line = lineNumber;
3436       bpRunToCursor.type = runToCursor;
3437       bpRunToCursor.enabled = true;
3438       bpRunToCursor.condition = null;
3439       bpRunToCursor.ignore = 0;
3440       bpRunToCursor.level = -1;
3441    }
3442
3443    ExpressionType ::DebugEvalExpTypeError(char * result)
3444    {
3445       if(result)
3446          return dummyExp;
3447       switch(eval.error)
3448       {
3449          case symbolNotFound:
3450             return symbolErrorExp;
3451          case memoryCantBeRead:
3452             return memoryErrorExp;
3453       }
3454       return unknownErrorExp;
3455    }
3456
3457    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3458    {
3459       char * result;
3460       if(ide.projectView && ide.debugger.state == stopped)
3461       {
3462          result = GdbEvaluateExpression(expression);
3463          *error = DebugEvalExpTypeError(result);
3464       }
3465       else
3466       {
3467          result = null;
3468          *error = noDebuggerErrorExp;
3469       }
3470       return result;
3471    }
3472
3473    char * ::ReadMemory(uint address, int size, char format, ExpressionType * error)
3474    {
3475       // check for state
3476       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3477       if(!result || !strcmp(result, "N/A"))
3478          *error = memoryErrorExp;
3479       else
3480          *error = DebugEvalExpTypeError(result);
3481       return result;
3482    }
3483 }
3484
3485 class GdbThread : Thread
3486 {
3487    Debugger debugger;
3488
3489    unsigned int Main()
3490    {
3491       static char output[4096];
3492       Array<char> dynamicBuffer { minAllocSize = 4096 };
3493       DualPipe oldGdbHandle = gdbHandle;
3494       incref oldGdbHandle;
3495
3496       app.Lock();
3497       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3498       {
3499          int result;
3500          app.Unlock();
3501          result = gdbHandle.Read(output, 1, sizeof(output));
3502          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3503             break;
3504          app.Lock();
3505          if(result)
3506          {
3507             int c;
3508             int start = 0;
3509
3510             for(c = 0; c<result; c++)
3511             {
3512                if(output[c] == '\n')
3513                {
3514                   int pos = dynamicBuffer.size;
3515                   dynamicBuffer.size += c - start;
3516                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3517                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3518                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3519                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3520                      dynamicBuffer.size++;
3521                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3522 #ifdef _DEBUG
3523                   // printf("%s\n", dynamicBuffer.array);
3524 #endif
3525                   debugger.GdbThreadMain(&dynamicBuffer[0]);
3526                   dynamicBuffer.size = 0;
3527                   start = c + 1;
3528                }
3529             }
3530             if(c == result)
3531             {
3532                int pos = dynamicBuffer.size;
3533                dynamicBuffer.size += c - start;
3534                memcpy(&dynamicBuffer[pos], output + start, c - start);
3535             }
3536          }
3537          else
3538          {
3539 #ifdef _DEBUG
3540             printf("Got end of file from GDB!\n");
3541 #endif
3542          }
3543       }
3544       delete dynamicBuffer;
3545       //if(oldGdbHandle == gdbHandle)
3546          debugger.GdbThreadExit();
3547       delete oldGdbHandle;
3548       app.Unlock();
3549       return 0;
3550    }
3551 }
3552
3553 #if defined(__unix__)
3554 #define uint _uint
3555 #include <errno.h>
3556 #include <stdio.h>
3557 #include <fcntl.h>
3558 #include <sys/types.h>
3559 #undef uint
3560
3561 File fifoFile;
3562
3563 class ProgramThread : Thread
3564 {
3565    bool terminate;
3566    unsigned int Main()
3567    {
3568       bool result = true;
3569       bool fileCreated = false;
3570       mode_t mask = 0600;
3571       static char output[1000];
3572       int fd;
3573
3574       /*if(!mkfifo(progFifoPath, mask))
3575       {
3576          fileCreated = true;
3577       }
3578       else
3579       {
3580          app.Lock();
3581          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3582          app.Unlock();
3583       }*/
3584
3585       if(FileExists(progFifoPath)) //fileCreated)
3586       {
3587          fifoFile = FileOpen(progFifoPath, read);
3588          if(!fifoFile)
3589          {
3590             app.Lock();
3591             ide.outputView.debugBox.Logf($"err: Unable to open FIFO %s for read\n", progFifoPath);
3592             app.Unlock();
3593          }
3594          else
3595          {
3596 #ifdef BSD
3597             // TODO: Fix on BSD 
3598             fd = 0;
3599 #else
3600             fd = fileno(fifoFile.input);
3601 #endif
3602             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3603          }
3604       }
3605
3606       while(!terminate && fifoFile && !fifoFile.Eof())
3607       {
3608          fd_set rs, es;
3609          struct timeval time;
3610          int selectResult;
3611          time.tv_sec = 1;
3612          time.tv_usec = 0;
3613          FD_ZERO(&rs);
3614          FD_ZERO(&es);
3615          FD_SET(fd, &rs);
3616          FD_SET(fd, &es);
3617          selectResult = select(fd + 1, &rs, null, null, &time);
3618          if(FD_ISSET(fd, &rs))
3619          {
3620             int result = read(fd, output, sizeof(output)-1);
3621             if(!result || (result < 0 && errno != EAGAIN))
3622                break;
3623             if(result > 0)
3624             {
3625                output[result] = '\0';
3626                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3627                {
3628                   app.Lock();
3629                   ide.outputView.debugBox.Log(output);
3630                   app.Unlock();
3631                }
3632             }
3633          }
3634       }
3635
3636       //if(fifoFile)
3637       {
3638          //fifoFile.CloseInput();
3639          //delete fifoFile;
3640          app.Lock();
3641          ide.outputView.debugBox.Log("\n");
3642          app.Unlock();
3643       }
3644       /*
3645       if(FileExists(progFifoPath)) //fileCreated)
3646       {
3647          DeleteFile(progFifoPath);
3648          progFifoPath[0] = '\0';
3649       }
3650       */
3651       return 0;
3652    }
3653 }
3654 #endif
3655
3656 class Argument : struct
3657 {
3658    Argument prev, next;
3659    char * name;
3660    char * value;
3661
3662    void Free()
3663    {
3664       delete name;
3665       delete value;
3666    }
3667
3668    ~Argument()
3669    {
3670       Free();
3671    }
3672 }
3673
3674 class Frame : struct
3675 {
3676    Frame prev, next;
3677    int level;
3678    char * addr;
3679    char * func;
3680    int argsCount;
3681    OldList args;
3682    char * from;
3683    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3684    char * file;
3685    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3686    char * absoluteFile;
3687    int line;
3688
3689    void Free()
3690    {
3691       delete addr;
3692       delete func;
3693       delete from;
3694       delete file;
3695       delete absoluteFile;
3696       args.Free(Argument::Free);
3697    }
3698
3699    ~Frame()
3700    {
3701       Free();
3702    }
3703 }
3704
3705 class GdbDataStop : struct
3706 {
3707    char * reason;
3708    int threadid;
3709    union
3710    {
3711       struct
3712       {
3713          int bkptno;
3714       };
3715       struct
3716       {
3717          char * name;
3718          char * meaning;
3719       };
3720       struct
3721       {
3722          char * gdbResultVar;
3723          char * returnValue;
3724       };
3725    };
3726    Frame frame { };
3727
3728    void Free()
3729    {
3730       if(reason)
3731       {
3732          if(!strcmp(reason, "signal-received"))
3733          {
3734             delete name;
3735             delete meaning;
3736          }
3737          else if(!strcmp(reason, "function-finished"))
3738          {
3739             delete gdbResultVar;
3740             delete returnValue;
3741          }
3742          delete reason;
3743       }
3744       if(frame) frame.Free();
3745    }
3746
3747    ~GdbDataStop()
3748    {
3749       Free();
3750    }
3751 }
3752
3753 class GdbDataBreakpoint : struct
3754 {
3755    int number;
3756    char * type;
3757    char * disp;
3758    bool enabled;
3759    char * addr;
3760    char * func;
3761    char * file;
3762    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3763    int line;
3764    char * at;
3765    int times;
3766
3767    void Free()
3768    {
3769       delete type;
3770       delete disp;
3771       delete addr;
3772       delete func;
3773       delete file;
3774       delete at;
3775    }
3776
3777    ~GdbDataBreakpoint()
3778    {
3779       Free();
3780    }
3781 }
3782
3783 class Breakpoint : struct
3784 {
3785    class_no_expansion;
3786
3787    char * relativeFilePath;
3788    char * absoluteFilePath;
3789    int line;
3790    bool enabled;
3791    int hits;
3792    int ignore;
3793    int level;
3794    Watch condition;
3795    bool inserted;
3796    BreakpointType type;
3797    DataRow row;
3798    
3799    GdbDataBreakpoint bp;
3800
3801    char * LocationToString()
3802    {
3803       char location[MAX_LOCATION+20];
3804       sprintf(location, "%s:%d", relativeFilePath, line);
3805 #if defined(__WIN32__)
3806       ChangeCh(location, '/', '\\');
3807 #endif
3808       return CopyString(location);
3809    }
3810
3811    void Save(File f)
3812    {
3813       if(relativeFilePath && relativeFilePath[0])
3814       {
3815          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3816          if(condition)
3817             f.Printf("       ~ %s\n", condition.expression);
3818       }
3819    }
3820
3821    void Free()
3822    {
3823       if(bp)
3824          bp.Free();
3825       delete bp;
3826       delete relativeFilePath;
3827       delete absoluteFilePath;
3828    }
3829
3830    ~Breakpoint()
3831    {
3832       Free();
3833    }
3834
3835 }
3836
3837 class Watch : struct
3838 {
3839    class_no_expansion;
3840    
3841    Type type;
3842    char * expression;
3843    char * value;
3844    DataRow row;
3845
3846    void Save(File f)
3847    {
3848       f.Printf("    ~ %s\n", expression);
3849    }
3850
3851    void Free()
3852    {
3853       delete expression;
3854       delete value;
3855       FreeType(type);
3856       type = null;
3857    }
3858
3859    void Reset()
3860    {
3861       delete value;
3862       FreeType(type);
3863       type = null;
3864    }
3865
3866    ~Watch()
3867    {
3868       Free();
3869    }
3870 }
3871
3872 class DebugListItem : struct
3873 {
3874    char * name;
3875    char * value;
3876 }
3877
3878 struct DebugEvaluationData
3879 {
3880    bool active;
3881    char * result;
3882    int bytes;
3883    uint nextBlockAddress;
3884
3885    DebuggerEvaluationError error;
3886 };
3887
3888 class CodeLocation : struct
3889 {
3890    char * file;
3891    char * absoluteFile;
3892    int line;
3893
3894    CodeLocation ::ParseCodeLocation(char * location)
3895    {
3896       if(location)
3897       {
3898          char * colon = null;
3899          char * temp;
3900          char loc[MAX_LOCATION];
3901          strcpy(loc, location);
3902          for(temp = loc; temp = strstr(temp, ":"); temp++)
3903             colon = temp;
3904          if(colon)
3905          {
3906             colon[0] = '\0';
3907             colon++;
3908             if(colon)
3909             {
3910                int line = atoi(colon);
3911                if(line)
3912                {
3913                   CodeLocation codloc { line = line };
3914                   codloc.file = CopyString(loc);
3915                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3916                   return codloc;
3917                }
3918             }
3919          }
3920       }
3921       return null;
3922    }
3923
3924    void Free()
3925    {
3926       delete file;
3927       delete absoluteFile;
3928    }
3929
3930    ~CodeLocation()
3931    {
3932       Free();
3933    }
3934 }