54360b3a7d76a95a42c70c47c4d874a9b7b67654
[gdbmicli.git] / main.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdarg.h>
4 #include <signal.h>
5 #include <readline/readline.h>
6 #include <readline/history.h>
7 #include <assert.h>
8 #include <errno.h>
9 #include "ansidecl.h"
10 #include "mi_gdb.h"
11
12 #define fatal(fmt...) fatal_func (__FILE__, __LINE__, __PRETTY_FUNCTION__, fmt)
13 #define gdb_assert assert
14 #define gdb_assert_not_reached(msg) fatal ("%s", (msg))
15
16 static ATTRIBUTE_NORETURN void
17 fatal_func (const char *file, int line, const char *func, const char *fmt, ...)
18 {
19   va_list ap;
20
21   fprintf (stderr, "%s:%d:%s:", file, line, func);
22   va_start (ap, fmt);
23   vfprintf (stderr, fmt, ap);
24   va_end (ap);
25   fputc ('\n', stderr);
26 raise (SIGABRT);
27   exit (EXIT_FAILURE);
28 }
29
30 static char *
31 xstrdup (const char *s)
32 {
33   char *retval;
34
35   gdb_assert (s != NULL);
36   retval = strdup (s);
37   gdb_assert (retval != NULL);
38   return retval;
39 }
40
41 static void *
42 xmalloc (size_t size)
43 {
44   void *p;
45
46   gdb_assert (size != 0);
47   p = malloc (size);
48   gdb_assert (p != NULL);
49   return p;
50 }
51
52 static void *
53 xrealloc (void *p, size_t size)
54 {
55   if (p == NULL)
56     return xmalloc (size);
57   gdb_assert (size != 0);
58   p = realloc (p, size);
59   gdb_assert (p != NULL);
60   return p;
61 }
62
63 static void
64 xfree (void *p)
65 {
66   free (p);
67 }
68
69 static char *
70 xstrprintf (const char *fmt, ...)
71 {
72   va_list ap;
73   char *retval;
74   int i;
75
76   va_start (ap, fmt);
77   i = vasprintf (&retval, fmt, ap);
78   va_end (ap);
79   gdb_assert (i > 0);
80   gdb_assert (i == strlen (retval));
81   return retval;
82 }
83
84 static void
85 console_cb (const char *str, void *data)
86 {
87   fputs (str, stdout);
88 }
89
90 static ATTRIBUTE_UNUSED void
91 to_gdb_cb (const char *str, void *data)
92 {
93   printf ("to_gdb: %s\n", str);
94 }
95
96 static ATTRIBUTE_UNUSED void
97 from_gdb_cb (const char *str, void *data)
98 {
99   printf ("from_gdb: %s\n", str);
100 }
101
102 static void
103 h_disconnect (int rc, void *arg)
104 {
105   mi_h *h = arg;
106
107   mi_disconnect (h);
108 }
109
110 static void
111 history_save (int rc, void *arg)
112 {
113   const char *history_filename = arg;
114
115   write_history (history_filename);
116 }
117
118 static void
119 quit_command (mi_h *h, const char *cmd)
120 {
121   exit (EXIT_SUCCESS);
122 }
123
124 static void
125 default_command (mi_h *h, const char *cmd)
126 {
127   int count = 1;
128
129   mi_send (h, "-interpreter-exec console \"%s\"\n", cmd);
130
131   while (count > 0)
132     {
133       mi_output *rec, *res;
134
135       res = mi_get_response_blk (h);
136       gdb_assert (res != NULL);
137
138       for (rec = res; rec != NULL; rec = rec->next)
139         switch (rec->tclass)
140         {
141           case MI_CL_DONE:
142             count--;
143             break;
144           case MI_CL_RUNNING:
145             /* We do not get MI_CL_DONE here, wait for MI_CL_STOPPED.  */
146             break;
147           case MI_CL_STOPPED:
148             count--;
149             break;
150           case MI_CL_ERROR:
151             count--;
152             gdb_assert (rec->c->type == t_const);
153             puts (rec->c->v.cstr);
154             break;
155           default:
156             fatal ("mi_get_rrecord == MI_CL_??? (%d)", rec->tclass);
157         }
158
159       mi_free_output (res);
160     }
161 }
162
163 static const struct cmd
164 {
165   const char *name;
166   void (*func) (mi_h *h, const char *cmd);
167 } cmds[] =
168 {
169   { "q", quit_command },
170   { "qu", quit_command },
171   { "qui", quit_command },
172   { "quit", quit_command },
173 };
174
175 static void
176 executecommand (mi_h *h, const char *cmd)
177 {
178   const char *start, *end, *cs;
179   const struct cmd *cmdp;
180
181   cs = cmd;
182   while (isspace (*cs))
183     cs++;
184   start = cs;
185   while (isalnum (*cs))
186     cs++;
187   end = cs;
188
189   for (cmdp = cmds; cmdp < &cmds[LENGTH (cmds)]; cmdp++)
190     if (strlen (cmdp->name) == end - start
191         && strncmp (cmd, cmdp->name, end - start) == 0)
192       return cmdp->func (h, cmd);
193
194   return default_command (h, cmd);
195 }
196
197 static void
198 gdb_done (mi_h *h, const char *command)
199 {
200   mi_output *res;
201
202   mi_send (h, "%s\n", command);
203   res = mi_get_response_blk (h);
204   gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
205               && res->c == NULL);
206   mi_free_output (res);
207 }
208
209 static void
210 gdb_set_string (mi_h *h, const char *setting, const char *value)
211 {
212   char *cmd = xstrprintf ("-gdb-set %s %s", setting, value);
213
214   gdb_done (h, cmd);
215   xfree (cmd);
216 }
217
218 static void
219 gdb_set_bool (mi_h *h, const char *setting, bool value)
220 {
221   gdb_set_string (h, setting, value ? "on" : "off");
222 }
223
224 static char *
225 gdb_show_string (mi_h *h, const char *setting)
226 {
227   mi_output *res;
228   char *retval;
229
230   mi_send (h, "-gdb-show %s\n", setting);
231   res = mi_get_response_blk (h);
232   gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
233               && res->c != NULL && res->c->next == NULL
234               && res->c->type == t_const && strcmp (res->c->var, "value") == 0);
235   retval = xstrdup (res->c->v.cstr);
236   mi_free_output (res);
237   return retval;
238 }
239
240 static int
241 gdb_show_int (mi_h *h, const char *setting)
242 {
243   char *string = gdb_show_string (h, setting);
244   long l;
245   int retval;
246   char *end;
247
248   errno = 0;
249   retval = l = strtol (string, &end, 10);
250   gdb_assert (errno == 0 && (end == NULL || *end == '\0') && retval == l);
251   xfree (string);
252   return retval;
253 }
254
255 static bool
256 gdb_show_bool (mi_h *h, const char *setting)
257 {
258   char *string = gdb_show_string (h, setting);
259   bool retval;
260
261   retval = strcmp (string, "on") == 0;
262   gdb_assert (retval || strcmp (string, "off") == 0);
263   xfree (string);
264   return retval;
265 }
266
267 static mi_h *completion_entry_function_h;
268
269 static char **completion_entry_function_data;
270 static size_t completion_entry_function_data_used;
271 static size_t completion_entry_function_data_allocated;
272
273 static void
274 completion_entry_function_console_cb (const char *str, void *data)
275 {
276   int line_start = (intptr_t) data;
277   const char *cs;
278   char *s;
279
280   if (completion_entry_function_data_used
281       == completion_entry_function_data_allocated)
282     {
283       if (completion_entry_function_data_allocated > 0)
284         completion_entry_function_data_allocated *= 2;
285       else
286         completion_entry_function_data_allocated = 0x100;
287       completion_entry_function_data = xrealloc (completion_entry_function_data,
288                                   (sizeof (*completion_entry_function_data)
289                                    * completion_entry_function_data_allocated));
290     }
291
292   cs = strchr (str, '\n');
293   if (cs == NULL || cs[1] != '\0')
294     fatal ("Invalid GDB data: %s", str);
295
296   if (strncmp (rl_line_buffer, str, rl_point) != 0)
297     fatal ("Completion GDB data do not match, have \"%.*s\", got \"%.*s\".",
298            (int) rl_point, rl_line_buffer, (int) (cs - str), str);
299
300   s = xmalloc (cs - str - line_start + 1);
301   memcpy (s, &str[line_start], cs - str - line_start);
302   s[cs - str - line_start] = '\0';
303   completion_entry_function_data[completion_entry_function_data_used++] = s;
304 }
305
306 static char *
307 completion_entry_function (const char *text, int matches)
308 {
309   mi_h *h = completion_entry_function_h;
310
311   gdb_assert (matches >= 0);
312   if (matches == 0)
313     {
314       mi_output *res;
315       int line_start;
316
317       while (completion_entry_function_data_used)
318         xfree (completion_entry_function_data
319                [--completion_entry_function_data_used]);
320       xfree (completion_entry_function_data);
321       completion_entry_function_data = NULL;
322       completion_entry_function_data_used = 0;
323       completion_entry_function_data_allocated = 0;
324
325       gdb_assert (rl_point >= 0);
326       gdb_assert (strlen (rl_line_buffer) >= rl_point);
327       gdb_assert (strlen (text) <= rl_point);
328       line_start = rl_point - strlen (text);
329       gdb_assert (strncmp (text, &rl_line_buffer[line_start],
330                            strlen (text)) == 0);
331       mi_send (h, "-interpreter-exec console \"complete %.*s\"\n",
332                (int) rl_point, rl_line_buffer);
333
334       mi_set_console_cb (h, completion_entry_function_console_cb,
335                          (void *) (intptr_t) line_start);
336       res = mi_get_response_blk (h);
337       gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE
338                   && res->c == NULL);
339       mi_free_output (res);
340       mi_set_console_cb (h, console_cb, NULL);
341     }
342
343   if (matches < completion_entry_function_data_used)
344     return xstrdup (completion_entry_function_data[matches]);
345   else if (matches == completion_entry_function_data_used)
346     return NULL;
347   else
348     gdb_assert_not_reached ("too many matches");
349 }
350
351 int
352 main (int argc, char **argv)
353 {
354   mi_h *h;
355   char *cmd;
356   const char *ex[argc];
357   unsigned ex_count = 0, ex_used = 0;
358
359   setbuf (stdout, NULL);
360
361   while (*++argv != NULL)
362     {
363       if (strcmp (*argv, "-ex") == 0 && argv[1] != NULL)
364         ex[ex_count++] = *++argv;
365       else
366         fatal ("Unknown parameter: %s", *argv);
367     }
368
369   mi_set_workaround (MI_PSYM_SEARCH, 0);
370
371   h = mi_connect_local ();
372   if (h == NULL)
373     fatal ("Cannot connect to GDB");
374
375   on_exit (h_disconnect, h);
376
377   mi_set_console_cb (h, console_cb, NULL);
378 //  mi_set_to_gdb_cb (h, to_gdb_cb, NULL);
379 //  mi_set_from_gdb_cb (h, from_gdb_cb, NULL);
380
381   completion_entry_function_h = h;
382   rl_completion_entry_function = completion_entry_function;
383   rl_readline_name = "gdb";        
384
385   if (gdb_show_bool (h, "history save"))
386     {
387       int history_size = gdb_show_int (h, "history size");
388       char *history_filename = gdb_show_string (h, "history filename");
389
390       gdb_set_bool (h, "history save", false);
391       stifle_history (history_size);
392       read_history (history_filename);
393       on_exit (history_save, history_filename);
394       /* Do not free HISTORY_FILENAME.  */
395     }
396
397   for (;;)
398     {
399       if (ex_used < ex_count)
400         {
401           cmd = xstrdup (ex[ex_used++]);
402           printf ("(gdb) %s\n", cmd);
403         }
404       else
405         {
406           cmd = readline ("(gdb) ");
407           if (cmd == NULL)
408             cmd = xstrdup ("quit");
409           else
410             add_history (cmd);
411         }
412       executecommand (h, cmd);
413       xfree (cmd);
414     }
415 }