cleaned all trailing white space from source files.
[sdk] / ide / src / debugger / GDBDialog.ec
1 import "ecere"
2 import "CodeEditor"
3
4 static uint TokenizeList(char * string, const uint maxTokens, const char seperator, char * tokens[])
5 {
6    uint count = 0;
7    uint level = 0;
8
9    bool quoted = false; //bool escaped = false;
10    char * start = null;
11
12    for(; *string && count < maxTokens; string++)
13    {
14       if(!start)
15          start = string;
16       if(quoted)
17       {
18          if(*string == '\"')
19             quoted = false;
20       }
21       else if(*string == '\"')
22          quoted = true;
23       else if(*string == '{' || *string == '[' || *string == '(' || *string == '<')
24          level++;
25       else if(*string == '}' || *string == ']' || *string == ')' || *string == '>')
26          level--;
27       else if(*string == seperator && !level)
28       {
29          tokens[count++] = start;
30          *string = '\0';
31          start = null;
32       }
33    }
34    if(start && count < maxTokens)
35    {
36       tokens[count++] = start;
37       *string = '\0';
38    }
39    return count;
40 }
41
42 // String Unescape Copy
43 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
44 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
45 static void struscpy(char * d, char * s)
46 {
47    int j, k;
48    j = k = 0;
49    while(s[j])
50    {
51       switch(s[j])
52       {
53          case '\\':
54             switch(s[++j])
55             {
56                case 'n':
57                   d[k] = '\n';
58                   break;
59                case 't':
60                   d[k] = '\t';
61                   break;
62                case 'a':
63                   d[k] = '\a';
64                   break;
65                case 'b':
66                   d[k] = '\b';
67                   break;
68                case 'f':
69                   d[k] = '\f';
70                   break;
71                case 'r':
72                   d[k] = '\r';
73                   break;
74                case 'v':
75                   d[k] = '\v';
76                   break;
77                case '\\':
78                   d[k] = '\\';
79                   break;
80                case '\"':
81                   d[k] = '\"';
82                   break;
83                default:
84                   d[k] = '\\';
85                   d[++k] = s[j];
86             }
87             break;
88          default:
89             d[k] = s[j];
90       }
91       ++j;
92       ++k;
93    }
94    d[k] = s[j];
95 }
96
97 static char * StripBrackets(char * string)
98 {
99    int length = strlen(string);
100    if(length > 1 && *string == '[' && string[length - 1] == ']')
101    {
102       *string = '\0';
103       string[length - 1] = '\0';
104       return ++string;
105    }
106    else
107       return string;
108 }
109
110 static char * StripCurlies(char * string)
111 {
112    int length = strlen(string);
113    if(length > 1 && *string == '{' && string[length - 1] == '}')
114    {
115       *string = '\0';
116       string[length - 1] = '\0';
117       return ++string;
118    }
119    else
120       return string;
121 }
122
123 class TempListItem : struct
124 {
125    char * name;
126    char * value;
127 }
128
129 static bool TokenizeListItem(char * string, TempListItem item)
130 {
131    char * equal = strstr(string, "=");
132    if(equal)
133    {
134       item.name = string;
135       *equal = '\0';
136       equal++;
137       item.value = equal;
138       equal = null;
139       return true;
140    }
141    else
142       return false;
143 }
144
145 class Output : struct
146 {
147 public:
148    Output prev, next;
149    char * output;
150    class_fixed
151
152    void Free()
153    {
154       delete output;
155    }
156 }
157
158 class Command : struct
159 {
160 public:
161    Command prev, next;
162    char * command;
163    OldList outputs;
164    class_fixed
165
166    void Free()
167    {
168       delete command;
169       outputs.Free(Output::Free);
170    }
171 }
172
173 /*class TreeStackItem : struct
174 {
175    int tokenCount;
176    char * tokens[3200];
177    DataRow parentRow;
178 }*/
179
180 class TagButton : public Button
181 {
182    void * tag;
183 }
184
185 class GDBDialog : Window
186 {
187    text = $"GDB Command";
188    background = formColor;
189    borderStyle = sizable;
190    hasMaximize = true;
191    hasMinimize = true;
192    hasClose = true;
193    stayOnTop = true;
194    size = { 800, 600 };
195    autoCreate = false;
196
197    Command lastCommand;
198    OldList commands;
199
200    Stacker bg
201    {
202       this, direction = horizontal, gap = 0;
203       isActiveClient = true;
204       background = darkGray;//formColor
205       anchor = { left = 0, top = 0, right = 0, bottom = 0 };
206       flipSpring = true;
207       flipper = rightCol;
208    };
209
210    Stacker leftCol
211    {
212       bg, this, gap = 4, margin = 4 ;
213       isActiveClient = true;
214       background = darkGray;
215       size = { 200, 100 };
216       anchor = { top = 0, bottom = 0 };
217       flipSpring = true;
218       flipper = history;
219    };
220
221    PaneSplitter splitter
222    {
223       this, leftPane = leftCol, rightPane = rightCol, split = 200;
224    };
225
226    Stacker rightCol
227    {
228       bg, this, gap = 4, margin = 4;
229       isActiveClient = true;
230       background = darkGray;
231       size = { 800, 600 };
232       anchor = { top = 0, bottom = 0 };
233       flipSpring = true;
234       flipper = tree;
235    };
236
237    Label lhistory { leftCol, this, position = { 4, 4 }, labeledWindow = history };
238    ListBox history
239    {
240       leftCol, this, borderStyle = deep, hasVertScroll = true, hasHorzScroll = true;
241       // selectionColor = unfocusedSelectorColor;
242       fullRowSelect = true, alwaysHighLight = true;
243
244       size = { 180 };
245       anchor = { left = leftCol.margin, right = leftCol.margin };
246       text = $"Command History";
247
248       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
249       {
250          if(row)
251          {
252             lastCommand = (Command)row.tag;
253             command.contents = lastCommand.command;
254             UpdateOutput();
255          }
256          return true;
257       }
258    };
259
260    Label commandLabel { rightCol, this, position = { 4, 4 }, labeledWindow = command };
261    EditBox command
262    {
263       rightCol, this, text = $"Command:", size = { 328, 19 }, anchor = { left = rightCol.margin, right = rightCol.margin };
264
265       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
266       {
267          if((SmartKey)key == up || (SmartKey)key == down)
268          {
269             if(commands.first)
270             {
271                bool previous = ((SmartKey)key == up);
272
273                if(!lastCommand)
274                   lastCommand = previous ? commands.last : commands.first;
275                else
276                   lastCommand = previous ? lastCommand.prev : lastCommand.next;
277
278                if(lastCommand)
279                {
280                   command.contents = lastCommand.command;
281                   UpdateOutput();
282                }
283                else
284                  lastCommand = previous ? commands.first : commands.last;
285             }
286          }
287          else if((SmartKey)key == enter)
288             OnCommand(command.contents);
289          return true;
290       }
291    };
292
293    Stacker toolBar
294    {
295       rightCol, this, direction = horizontal, gap = 4;
296       isActiveClient = true;
297       background = darkGray;
298       size = { 200, 30 };
299       anchor = { left = rightCol.margin, right = rightCol.margin };
300    };
301
302    TagButton infoLibs { toolBar, this, text = "libs", tag = "info shared", NotifyClicked = QuickCommandNotifyClicked; };
303    TagButton infoPaths { toolBar, this, text = "paths", tag = "-environment-path", NotifyClicked = QuickCommandNotifyClicked; };
304    TagButton infoWorkDir { toolBar, this, text = "wd", tag = "-environment-pwd", NotifyClicked = QuickCommandNotifyClicked; };
305    TagButton infoDirs { toolBar, this, text = "pths", tag = "-environment-directory", NotifyClicked = QuickCommandNotifyClicked; };
306    TagButton infoTemp { toolBar, this, text = "pths", tag = "-environment-temp", NotifyClicked = QuickCommandNotifyClicked; };
307
308    bool QuickCommandNotifyClicked(Button button, int x, int y, Modifiers mods)
309    {
310       TagButton tagButton = (TagButton)button;
311       OnCommand(tagButton.tag);
312       return true;
313    }
314
315    Label outputLabel { rightCol, this, position = { 4, 4 }, labeledWindow = output };
316    EditBox output
317    {
318       rightCol, this, text = $"Output:", multiLine = true, hasVertScroll = true, hasHorzScroll = true;
319       size = { 328, 80 };
320       anchor = { left = rightCol.margin, right = rightCol.margin };
321       font = { panelFont.faceName, panelFont.size };
322    };
323
324    Label treeLabel { rightCol, this, position = { 4, 4 }, labeledWindow = tree };
325    ListBox tree
326    {
327       rightCol, this, text = $"Tree:";
328       multiSelect = false, fullRowSelect = false, hasVertScroll = true, hasHorzScroll = true;
329       borderStyle = deep, collapseControl = true, treeBranches = true;
330       anchor = { left = rightCol.margin, right = rightCol.margin };
331       font = { panelFont.faceName, panelFont.size };
332    };
333
334    void UpdateOutput()
335    {
336       Output out;
337       output.Clear();
338       for(out = lastCommand.outputs.first; out; out = out.next)
339       {
340          output.AddS(out.output);
341          output.AddS("\n");
342       }
343       UpdateTree();
344    }
345
346    void UpdateTree()
347    {
348       if(lastCommand && lastCommand.outputs.first)
349       {
350          int c;
351          char * s, * t;
352          int i, j, outTksCount, subTksCount;
353          char * outTokens[3200], * subTokens[3200];
354          DataRow root, frame, row;
355          TempListItem item { };
356          Output out;
357
358          tree.Clear();
359          root = tree.AddString("Output");
360
361          for(out = lastCommand.outputs.first; out; out = out.next)
362          {
363             if(out.output && out.output[0])
364             {
365                t = CopyString(out.output);
366                s = t;
367
368                switch(s[0])
369                {
370                   case '^':
371                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
372                      row = root.AddString(outTokens[0]);
373                      if(!strcmp(outTokens[0], "^done"))
374                      {
375                         for(i = 1; i < outTksCount; i++)
376                         {
377                            if(TokenizeListItem(outTokens[i], item))
378                            {
379                               if(!strcmp(item.name, "bkpt"))
380                               {
381                                  row = root.AddString(item.name);
382                                  item.value = StripCurlies(item.value);
383                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ',', subTokens);
384                                  for(j = 0; j < subTksCount; j++)
385                                  {
386                                     if(TokenizeListItem(subTokens[j], item))
387                                        UpdateTreeValue(item, row);
388                                     else
389                                        row.AddString("Bad");
390                                  }
391                               }
392                               else if(!strcmp(item.name, "stack"))
393                               {
394                                  row = root.AddString(item.name);
395                                  item.value = StripBrackets(item.value);
396                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ',', subTokens);
397                                  for(j = 0; j < subTksCount; j++)
398                                  {
399                                     if(TokenizeListItem(subTokens[j], item))
400                                     {
401                                        frame = row.AddString(item.name);
402                                        if(!strcmp(item.name, "frame"))
403                                           UpdateTreeFrame(item, frame);
404                                        else
405                                           UpdateTreeValue(item, row);
406                                     }
407                                     else
408                                        row.AddString("Bad");
409                                  }
410                               }
411                               else if(!strcmp(item.name, "path"))
412                               {
413                                  StripQuotes(item.value, item.value);
414                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ':', subTokens);
415                                  for(j = 0; j < subTksCount; j++)
416                                  {
417                                     row = root.AddString(subTokens[j]);
418                                  }
419                               }
420                               else
421                                  UpdateTreeValue(item, row);
422                            }
423                            else
424                               row.AddString("Bad");
425                         }
426                      }
427                      else if(!strcmp(outTokens[0], "^running"))
428                      {
429                         if(outTksCount > 1)
430                            row.AddString(outTokens[1]);
431                      }
432                      else if(!strcmp(outTokens[0], "^exit"))
433                      {
434                         if(outTksCount > 1)
435                            row.AddString(outTokens[1]);
436                      }
437                      else if(!strcmp(outTokens[0], "^error"))
438                      {
439                         if(outTksCount > 1)
440                            row.AddString(outTokens[1]);
441                      }
442                      else
443                      {
444                         row = row.AddString("Unknown output");
445                         if(outTksCount > 1)
446                            row.AddString(outTokens[1]);
447                      }
448                      break;
449                   case '+':
450                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
451                      row = root.AddString(outTokens[0]);
452                      {
453                         row = root.AddString("Unknown status-async-output");
454                         row.AddString(s);
455                      }
456                      break;
457                   case '=':
458                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
459                      row = root.AddString(outTokens[0]);
460                      //if(!strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
461                      //else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
462                      //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"
463                      if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=library-loaded"))
464                      {
465                         for(i = 1; i < outTksCount; i++)
466                            row.AddString(outTokens[i]);
467                      }
468                      else
469                      {
470                         row = root.AddString("Unknown notify-async-output");
471                         row.AddString(s);
472                      }
473                      break;
474                   case '*':
475                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
476                      row = root.AddString(outTokens[0]);
477                      if(!strcmp(outTokens[0], "*stopped"))
478                      {
479                         for(i = 1; i < outTksCount; i++)
480                         {
481                            if(TokenizeListItem(outTokens[i], item))
482                            {
483                               if(!strcmp(item.name, "frame"))
484                               {
485                                  frame = row.AddString(item.name);
486                                  UpdateTreeFrame(item, frame);
487                               }
488                               UpdateTreeValue(item, row);
489                            }
490                            else
491                               row.AddString("Bad");
492                         }
493                      }
494                      else
495                      {
496                         row = root.AddString("Unknown exec-async-output");
497                         if(outTksCount > 1)
498                            row.AddString(outTokens[1]);
499                      }
500                      break;
501                   case '(':
502                      if(!strcmpi(s, "(gdb) "))
503                      {
504                         root.AddString(s);
505                      }
506                      else
507                      {
508                         row = root.AddString("Unknown prompt");
509                         row.AddString(s);
510                      }
511                      break;
512                   case '~':
513                      s++;
514                      StripQuotes(s, s);
515                      if(TrimEscapedNewLineChar(s))
516                      {
517                         s = StripBrackets(s);
518                         if(!strncmp(s, "Executable and object file path: ", 33))
519                         {
520                            s += 33;
521                            outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ':', outTokens);
522                            for(i = 0; i < outTksCount; i++)
523                            {
524                               row = root.AddString(outTokens[i]);
525                            }
526                         }
527                         else
528                            root.AddString(s);
529                      }
530                      break;
531                   case '&':
532                      s++;
533                      StripQuotes(s, s);
534                      if(TrimEscapedNewLineChar(s))
535                      {
536                         struscpy(s, s);
537                         root.AddString(s);
538                      }
539                      break;
540                   default:
541                      row = root.AddString("Unknown output");
542                      row.AddString(s);
543                }
544
545                delete t;
546             }
547          }
548          delete item;
549       }
550    }
551
552    void UpdateTreeValue(TempListItem item, DataRow row)
553    {
554       char * string;
555       StripQuotes(item.value, item.value);
556       string = new char[strlen(item.name) + strlen(item.value) + 3];
557       sprintf(string, "%s: %s", item.name, item.value);
558       row.AddString(string);
559       delete string;
560    }
561
562    void UpdateTreeFrame(TempListItem item, DataRow frame)
563    {
564       int k, l, m, frameTksCount, argsTksCount, mTksCount;
565       char * frameTokens[3200], * argsTokens[3200], * mTokens[3200];
566
567       item.value = StripCurlies(item.value);
568       frameTksCount = TokenizeList(item.value, sizeof(frameTokens) / sizeof(char *), ',', frameTokens);
569       for(k = 0; k < frameTksCount; k++)
570       {
571          if(TokenizeListItem(frameTokens[k], item))
572          {
573             if(!strcmp(item.name, "level"))
574             {
575                char * string;
576                StripQuotes(item.value, item.value);
577                string = new char[strlen(item.value) + strlen(frame.string) + 7];
578                // WTH do I need to do in order to print with leading zeros
579                sprintf(string, "%d %s", atoi(item.value), frame.string);
580                frame.string = string;
581                delete string;
582             }
583             else if(!strcmp(item.name, "addr"))
584             {
585                char * string;
586                StripQuotes(item.value, item.value);
587                string = new char[strlen(item.value) + strlen(frame.string) + 4];
588                sprintf(string, "%s (%s)", frame.string, item.value);
589                frame.string = string;
590                delete string;
591             }
592             else if(!strcmp(item.name, "args"))
593             {
594                DataRow args, arg;
595                args = frame.AddString("args");
596                item.value = StripBrackets(item.value);
597                argsTksCount = TokenizeList(item.value, sizeof(argsTokens) / sizeof(char *), ',', argsTokens);
598                for(l = 0; l < argsTksCount; l++)
599                {
600                   arg = args.AddString("arg");
601                   argsTokens[l] = StripCurlies(argsTokens[l]);
602                   mTksCount = TokenizeList(argsTokens[l], sizeof(mTokens) / sizeof(char *), ',', mTokens);
603                   for(m = 0; m < mTksCount; m++)
604                   {
605                      if(TokenizeListItem(mTokens[m], item))
606                      {
607                         if(!strcmp(item.name, "name"))
608                         {
609                            char * string;
610                            StripQuotes(item.value, item.value);
611                            string = new char[strlen(arg.string) + strlen(item.value) + 2];
612                            sprintf(string, "%s %s", arg.string, item.value);
613                            arg.string = string;
614                            delete string;
615                         }
616                         else if(!strcmp(item.name, "value"))
617                         {
618                            char * string;
619                            StripQuotes(item.value, item.value);
620                            string = new char[strlen(arg.string) + strlen(item.value) + 4];
621                            sprintf(string, "%s = %s", arg.string, item.value);
622                            arg.string = string;
623                            delete string;
624                         }
625                         else
626                            UpdateTreeValue(item, args);
627                      }
628                      else
629                         frame.AddString("Bad");
630                   }
631                }
632             }
633             else
634                UpdateTreeValue(item, frame);
635          }
636          else
637             frame.AddString("Bad");
638       }
639    }
640
641    int TrimEscapedNewLineChar(char * s)
642    {
643       int len = strlen(s);
644       if(s[len - 1] == 'n' && s[len - 2] == '\\')
645       {
646          s[len - 2] = '\0';
647          len -= 2;
648       }
649       return len;
650    }
651
652    void AddCommand(char * string)
653    {
654       if(string && strlen(string))
655       {
656          Command cmd = Command { command = CopyString(string) };
657          commands.Add(cmd);
658          history.AddString(string).tag = (uint64)cmd;
659          lastCommand = commands.last;
660
661          if(created)
662          {
663             command.contents = string;
664             command.SelectAll();
665          }
666       }
667    }
668
669    void AddOutput(char * string)
670    {
671       if(string && strlen(string))
672       {
673          Command last = commands.last;
674          if(last)
675          {
676             last.outputs.Add(Output { output = CopyString(string) });
677             if(last == lastCommand)
678                UpdateOutput();
679          }
680       }
681    }
682
683    virtual void OnCommand(char * string);
684
685    void Show()
686    {
687       if(!created)
688          Create();
689       else
690          Activate();
691    }
692
693    bool OnPostCreate()
694    {
695       if(lastCommand)
696       {
697          command.contents = lastCommand.command;
698          UpdateOutput();
699       }
700       return true;
701    }
702
703    ~GDBDialog()
704    {
705       commands.Free(Command::Free);
706    }
707 }