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