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