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