ide: Copying bold and size attributes for fonts
[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    const 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)(intptr)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, panelFont.bold, panelFont.italic };
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, panelFont.bold, panelFont.italic };
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          char * s, * t;
351          int i, j, outTksCount, subTksCount;
352          char * outTokens[3200], * subTokens[3200];
353          DataRow root, frame, row;
354          TempListItem item { };
355          Output out;
356
357          tree.Clear();
358          root = tree.AddString("Output");
359
360          for(out = lastCommand.outputs.first; out; out = out.next)
361          {
362             if(out.output && out.output[0])
363             {
364                t = CopyString(out.output);
365                s = t;
366
367                switch(s[0])
368                {
369                   case '^':
370                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
371                      row = root.AddString(outTokens[0]);
372                      if(!strcmp(outTokens[0], "^done"))
373                      {
374                         for(i = 1; i < outTksCount; i++)
375                         {
376                            if(TokenizeListItem(outTokens[i], item))
377                            {
378                               if(!strcmp(item.name, "bkpt"))
379                               {
380                                  row = root.AddString(item.name);
381                                  item.value = StripCurlies(item.value);
382                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ',', subTokens);
383                                  for(j = 0; j < subTksCount; j++)
384                                  {
385                                     if(TokenizeListItem(subTokens[j], item))
386                                        UpdateTreeValue(item, row);
387                                     else
388                                        row.AddString("Bad");
389                                  }
390                               }
391                               else if(!strcmp(item.name, "stack"))
392                               {
393                                  row = root.AddString(item.name);
394                                  item.value = StripBrackets(item.value);
395                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ',', subTokens);
396                                  for(j = 0; j < subTksCount; j++)
397                                  {
398                                     if(TokenizeListItem(subTokens[j], item))
399                                     {
400                                        frame = row.AddString(item.name);
401                                        if(!strcmp(item.name, "frame"))
402                                           UpdateTreeFrame(item, frame);
403                                        else
404                                           UpdateTreeValue(item, row);
405                                     }
406                                     else
407                                        row.AddString("Bad");
408                                  }
409                               }
410                               else if(!strcmp(item.name, "path"))
411                               {
412                                  StripQuotes(item.value, item.value);
413                                  subTksCount = TokenizeList(item.value, sizeof(subTokens) / sizeof(char *), ':', subTokens);
414                                  for(j = 0; j < subTksCount; j++)
415                                  {
416                                     row = root.AddString(subTokens[j]);
417                                  }
418                               }
419                               else
420                                  UpdateTreeValue(item, row);
421                            }
422                            else
423                               row.AddString("Bad");
424                         }
425                      }
426                      else if(!strcmp(outTokens[0], "^running"))
427                      {
428                         if(outTksCount > 1)
429                            row.AddString(outTokens[1]);
430                      }
431                      else if(!strcmp(outTokens[0], "^exit"))
432                      {
433                         if(outTksCount > 1)
434                            row.AddString(outTokens[1]);
435                      }
436                      else if(!strcmp(outTokens[0], "^error"))
437                      {
438                         if(outTksCount > 1)
439                            row.AddString(outTokens[1]);
440                      }
441                      else
442                      {
443                         row = row.AddString("Unknown output");
444                         if(outTksCount > 1)
445                            row.AddString(outTokens[1]);
446                      }
447                      break;
448                   case '+':
449                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
450                      row = root.AddString(outTokens[0]);
451                      {
452                         row = root.AddString("Unknown status-async-output");
453                         row.AddString(s);
454                      }
455                      break;
456                   case '=':
457                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
458                      row = root.AddString(outTokens[0]);
459                      //if(!strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
460                      //else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
461                      //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"
462                      if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=library-loaded"))
463                      {
464                         for(i = 1; i < outTksCount; i++)
465                            row.AddString(outTokens[i]);
466                      }
467                      else
468                      {
469                         row = root.AddString("Unknown notify-async-output");
470                         row.AddString(s);
471                      }
472                      break;
473                   case '*':
474                      outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ',', outTokens);
475                      row = root.AddString(outTokens[0]);
476                      if(!strcmp(outTokens[0], "*stopped"))
477                      {
478                         for(i = 1; i < outTksCount; i++)
479                         {
480                            if(TokenizeListItem(outTokens[i], item))
481                            {
482                               if(!strcmp(item.name, "frame"))
483                               {
484                                  frame = row.AddString(item.name);
485                                  UpdateTreeFrame(item, frame);
486                               }
487                               UpdateTreeValue(item, row);
488                            }
489                            else
490                               row.AddString("Bad");
491                         }
492                      }
493                      else
494                      {
495                         row = root.AddString("Unknown exec-async-output");
496                         if(outTksCount > 1)
497                            row.AddString(outTokens[1]);
498                      }
499                      break;
500                   case '(':
501                      if(!strcmpi(s, "(gdb) "))
502                      {
503                         root.AddString(s);
504                      }
505                      else
506                      {
507                         row = root.AddString("Unknown prompt");
508                         row.AddString(s);
509                      }
510                      break;
511                   case '~':
512                      s++;
513                      StripQuotes(s, s);
514                      if(TrimEscapedNewLineChar(s))
515                      {
516                         s = StripBrackets(s);
517                         if(!strncmp(s, "Executable and object file path: ", 33))
518                         {
519                            s += 33;
520                            outTksCount = TokenizeList(s, sizeof(outTokens) / sizeof(char *), ':', outTokens);
521                            for(i = 0; i < outTksCount; i++)
522                            {
523                               row = root.AddString(outTokens[i]);
524                            }
525                         }
526                         else
527                            root.AddString(s);
528                      }
529                      break;
530                   case '&':
531                      s++;
532                      StripQuotes(s, s);
533                      if(TrimEscapedNewLineChar(s))
534                      {
535                         struscpy(s, s);
536                         root.AddString(s);
537                      }
538                      break;
539                   default:
540                      row = root.AddString("Unknown output");
541                      row.AddString(s);
542                }
543
544                delete t;
545             }
546          }
547          delete item;
548       }
549    }
550
551    void UpdateTreeValue(TempListItem item, DataRow row)
552    {
553       char * string;
554       StripQuotes(item.value, item.value);
555       string = new char[strlen(item.name) + strlen(item.value) + 3];
556       sprintf(string, "%s: %s", item.name, item.value);
557       row.AddString(string);
558       delete string;
559    }
560
561    void UpdateTreeFrame(TempListItem item, DataRow frame)
562    {
563       int k, l, m, frameTksCount, argsTksCount, mTksCount;
564       char * frameTokens[3200], * argsTokens[3200], * mTokens[3200];
565
566       item.value = StripCurlies(item.value);
567       frameTksCount = TokenizeList(item.value, sizeof(frameTokens) / sizeof(char *), ',', frameTokens);
568       for(k = 0; k < frameTksCount; k++)
569       {
570          if(TokenizeListItem(frameTokens[k], item))
571          {
572             if(!strcmp(item.name, "level"))
573             {
574                char * string;
575                StripQuotes(item.value, item.value);
576                string = new char[strlen(item.value) + strlen(frame.string) + 7];
577                // WTH do I need to do in order to print with leading zeros
578                sprintf(string, "%d %s", atoi(item.value), frame.string);
579                frame.string = string;
580                delete string;
581             }
582             else if(!strcmp(item.name, "addr"))
583             {
584                char * string;
585                StripQuotes(item.value, item.value);
586                string = new char[strlen(item.value) + strlen(frame.string) + 4];
587                sprintf(string, "%s (%s)", frame.string, item.value);
588                frame.string = string;
589                delete string;
590             }
591             else if(!strcmp(item.name, "args"))
592             {
593                DataRow args, arg;
594                args = frame.AddString("args");
595                item.value = StripBrackets(item.value);
596                argsTksCount = TokenizeList(item.value, sizeof(argsTokens) / sizeof(char *), ',', argsTokens);
597                for(l = 0; l < argsTksCount; l++)
598                {
599                   arg = args.AddString("arg");
600                   argsTokens[l] = StripCurlies(argsTokens[l]);
601                   mTksCount = TokenizeList(argsTokens[l], sizeof(mTokens) / sizeof(char *), ',', mTokens);
602                   for(m = 0; m < mTksCount; m++)
603                   {
604                      if(TokenizeListItem(mTokens[m], item))
605                      {
606                         if(!strcmp(item.name, "name"))
607                         {
608                            char * string;
609                            StripQuotes(item.value, item.value);
610                            string = new char[strlen(arg.string) + strlen(item.value) + 2];
611                            sprintf(string, "%s %s", arg.string, item.value);
612                            arg.string = string;
613                            delete string;
614                         }
615                         else if(!strcmp(item.name, "value"))
616                         {
617                            char * string;
618                            StripQuotes(item.value, item.value);
619                            string = new char[strlen(arg.string) + strlen(item.value) + 4];
620                            sprintf(string, "%s = %s", arg.string, item.value);
621                            arg.string = string;
622                            delete string;
623                         }
624                         else
625                            UpdateTreeValue(item, args);
626                      }
627                      else
628                         frame.AddString("Bad");
629                   }
630                }
631             }
632             else
633                UpdateTreeValue(item, frame);
634          }
635          else
636             frame.AddString("Bad");
637       }
638    }
639
640    int TrimEscapedNewLineChar(char * s)
641    {
642       int len = strlen(s);
643       if(s[len - 1] == 'n' && s[len - 2] == '\\')
644       {
645          s[len - 2] = '\0';
646          len -= 2;
647       }
648       return len;
649    }
650
651    void AddCommand(char * string)
652    {
653       if(string && strlen(string))
654       {
655          Command cmd = Command { command = CopyString(string) };
656          commands.Add(cmd);
657          history.AddString(string).tag = (uint64)(uintptr)cmd;
658          lastCommand = commands.last;
659
660          if(created)
661          {
662             command.contents = string;
663             command.SelectAll();
664          }
665       }
666    }
667
668    void AddOutput(char * string)
669    {
670       if(string && strlen(string))
671       {
672          Command last = commands.last;
673          if(last)
674          {
675             last.outputs.Add(Output { output = CopyString(string) });
676             if(last == lastCommand)
677                UpdateOutput();
678          }
679       }
680    }
681
682    virtual void OnCommand(const char * string);
683
684    void Show()
685    {
686       if(!created)
687          Create();
688       else
689          Activate();
690    }
691
692    bool OnPostCreate()
693    {
694       if(lastCommand)
695       {
696          command.contents = lastCommand.command;
697          UpdateOutput();
698       }
699       return true;
700    }
701
702    ~GDBDialog()
703    {
704       commands.Free(Command::Free);
705    }
706 }