Eliminate make_cleanup_ui_file_delete / make ui_file a class hierarchy
[external/binutils.git] / gdb / cli / cli-logging.c
1 /* Command-line output logging for GDB, the GNU debugger.
2
3    Copyright (C) 2003-2017 Free Software Foundation, Inc.
4
5    This file is part of GDB.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #include "defs.h"
21 #include "gdbcmd.h"
22 #include "ui-out.h"
23 #include "interps.h"
24
25 /* These hold the pushed copies of the gdb output files.
26    If NULL then nothing has yet been pushed.  */
27 struct saved_output_files
28 {
29   struct ui_file *out;
30   struct ui_file *err;
31   struct ui_file *log;
32   struct ui_file *targ;
33   struct ui_file *targerr;
34 };
35 static struct saved_output_files saved_output;
36 static char *saved_filename;
37
38 static char *logging_filename;
39 static void
40 show_logging_filename (struct ui_file *file, int from_tty,
41                        struct cmd_list_element *c, const char *value)
42 {
43   fprintf_filtered (file, _("The current logfile is \"%s\".\n"),
44                     value);
45 }
46
47 static int logging_overwrite;
48
49 static void
50 set_logging_overwrite (char *args, int from_tty, struct cmd_list_element *c)
51 {
52   if (saved_filename)
53     warning (_("Currently logging to %s.  Turn the logging off and on to "
54                "make the new setting effective."), saved_filename);
55 }
56
57 static void
58 show_logging_overwrite (struct ui_file *file, int from_tty,
59                         struct cmd_list_element *c, const char *value)
60 {
61   fprintf_filtered (file,
62                     _("Whether logging overwrites or "
63                       "appends to the log file is %s.\n"),
64                     value);
65 }
66
67 /* Value as configured by the user.  */
68 static int logging_redirect;
69
70 /* The on-disk file in use if logging is currently active together
71    with redirection turned off (and therefore using tee_file_new).
72    For active logging with redirection the on-disk file is directly in
73    GDB_STDOUT and this variable is NULL.  */
74 static struct ui_file *logging_no_redirect_file;
75
76 static void
77 set_logging_redirect (char *args, int from_tty, struct cmd_list_element *c)
78 {
79   ui_file_up destroy_old_stdout;
80   struct ui_file *output, *new_logging_no_redirect_file;
81   struct ui_out *uiout = current_uiout;
82
83   if (saved_filename == NULL
84       || (logging_redirect != 0 && logging_no_redirect_file == NULL)
85       || (logging_redirect == 0 && logging_no_redirect_file != NULL))
86     return;
87
88   if (logging_redirect != 0)
89     {
90       gdb_assert (logging_no_redirect_file != NULL);
91
92       /* ui_out_redirect still has not been called for next
93          gdb_stdout.  */
94       destroy_old_stdout.reset (gdb_stdout);
95
96       output = logging_no_redirect_file;
97       new_logging_no_redirect_file = NULL;
98
99       if (from_tty)
100         fprintf_unfiltered (saved_output.out, "Redirecting output to %s.\n",
101                             logging_filename);
102     }
103   else
104     {
105       gdb_assert (logging_no_redirect_file == NULL);
106       output = new tee_file (saved_output.out, 0, gdb_stdout, 0);
107       new_logging_no_redirect_file = gdb_stdout;
108
109       if (from_tty)
110         fprintf_unfiltered (saved_output.out, "Copying output to %s.\n",
111                             logging_filename);
112     }
113
114   /* Give the current interpreter a chance to do anything special that
115      it might need for logging, such as updating other channels.  */
116   if (current_interp_set_logging (1, output, NULL) == 0)
117     {
118       gdb_stdout = output;
119       gdb_stdlog = output;
120       gdb_stderr = output;
121       gdb_stdtarg = output;
122       gdb_stdtargerr = output;
123     }
124
125   logging_no_redirect_file = new_logging_no_redirect_file;
126
127   /* There is a former output pushed on the ui_out_redirect stack.  We
128      want to replace it by OUTPUT so we must pop the former value
129      first.  Ideally, we should either do both the pop and push or do
130      neither of them.  */
131
132   uiout->redirect (NULL);
133   uiout->redirect (output);
134 }
135
136 static void
137 show_logging_redirect (struct ui_file *file, int from_tty,
138                        struct cmd_list_element *c, const char *value)
139 {
140   fprintf_filtered (file, _("The logging output mode is %s.\n"), value);
141 }
142
143 /* If we've pushed output files, close them and pop them.  */
144 static void
145 pop_output_files (void)
146 {
147   if (logging_no_redirect_file)
148     {
149       delete logging_no_redirect_file;
150       logging_no_redirect_file = NULL;
151     }
152
153   if (current_interp_set_logging (0, NULL, NULL) == 0)
154     {
155       /* Only delete one of the files -- they are all set to the same
156          value.  */
157       delete gdb_stdout;
158
159       gdb_stdout = saved_output.out;
160       gdb_stderr = saved_output.err;
161       gdb_stdlog = saved_output.log;
162       gdb_stdtarg = saved_output.targ;
163       gdb_stdtargerr = saved_output.targerr;
164     }
165
166   saved_output.out = NULL;
167   saved_output.err = NULL;
168   saved_output.log = NULL;
169   saved_output.targ = NULL;
170   saved_output.targerr = NULL;
171
172   /* Stay consistent with handle_redirections.  */
173   if (!current_uiout->is_mi_like_p ())
174     current_uiout->redirect (NULL);
175 }
176
177 /* This is a helper for the `set logging' command.  */
178 static void
179 handle_redirections (int from_tty)
180 {
181   ui_file_up output;
182   ui_file_up no_redirect_file;
183
184   if (saved_filename != NULL)
185     {
186       fprintf_unfiltered (gdb_stdout, "Already logging to %s.\n",
187                           saved_filename);
188       return;
189     }
190
191   stdio_file_up log (new stdio_file ());
192   if (!log->open (logging_filename, logging_overwrite ? "w" : "a"))
193     perror_with_name (_("set logging"));
194
195   /* Redirects everything to gdb_stdout while this is running.  */
196   if (!logging_redirect)
197     {
198       no_redirect_file = std::move (log);
199       output.reset (new tee_file (gdb_stdout, 0, no_redirect_file.get (), 0));
200
201       if (from_tty)
202         fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n",
203                             logging_filename);
204     }
205   else
206     {
207       gdb_assert (logging_no_redirect_file == NULL);
208       output = std::move (log);
209
210       if (from_tty)
211         fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n",
212                             logging_filename);
213     }
214
215   saved_filename = xstrdup (logging_filename);
216   saved_output.out = gdb_stdout;
217   saved_output.err = gdb_stderr;
218   saved_output.log = gdb_stdlog;
219   saved_output.targ = gdb_stdtarg;
220   saved_output.targerr = gdb_stdtargerr;
221
222   /* Let the interpreter do anything it needs.  */
223   if (current_interp_set_logging (1, output.get (),
224                                   no_redirect_file.get ()) == 0)
225     {
226       gdb_stdout = output.get ();
227       gdb_stdlog = output.get ();
228       gdb_stderr = output.get ();
229       gdb_stdtarg = output.get ();
230       gdb_stdtargerr = output.get ();
231     }
232
233   output.release ();
234   logging_no_redirect_file = no_redirect_file.release ();
235
236   /* Don't do the redirect for MI, it confuses MI's ui-out scheme.  */
237   if (!current_uiout->is_mi_like_p ())
238     current_uiout->redirect (gdb_stdout);
239 }
240
241 static void
242 set_logging_on (char *args, int from_tty)
243 {
244   char *rest = args;
245
246   if (rest && *rest)
247     {
248       xfree (logging_filename);
249       logging_filename = xstrdup (rest);
250     }
251   handle_redirections (from_tty);
252 }
253
254 static void 
255 set_logging_off (char *args, int from_tty)
256 {
257   if (saved_filename == NULL)
258     return;
259
260   pop_output_files ();
261   if (from_tty)
262     fprintf_unfiltered (gdb_stdout, "Done logging to %s.\n", saved_filename);
263   xfree (saved_filename);
264   saved_filename = NULL;
265 }
266
267 static void
268 set_logging_command (char *args, int from_tty)
269 {
270   printf_unfiltered (_("\"set logging\" lets you log output to a file.\n"
271                        "Usage: set logging on [FILENAME]\n"
272                        "       set logging off\n"
273                        "       set logging file FILENAME\n"
274                        "       set logging overwrite [on|off]\n"
275                        "       set logging redirect [on|off]\n"));
276 }
277
278 static void
279 show_logging_command (char *args, int from_tty)
280 {
281   if (saved_filename)
282     printf_unfiltered (_("Currently logging to \"%s\".\n"), saved_filename);
283   if (saved_filename == NULL
284       || strcmp (logging_filename, saved_filename) != 0)
285     printf_unfiltered (_("Future logs will be written to %s.\n"),
286                        logging_filename);
287
288   if (logging_overwrite)
289     printf_unfiltered (_("Logs will overwrite the log file.\n"));
290   else
291     printf_unfiltered (_("Logs will be appended to the log file.\n"));
292
293   if (saved_filename)
294     {
295       if (logging_redirect)
296         printf_unfiltered (_("Output is being sent only to the log file.\n"));
297       else
298         printf_unfiltered (_("Output is being logged and displayed.\n"));
299     }
300   else
301     {
302       if (logging_redirect)
303         printf_unfiltered (_("Output will be sent only to the log file.\n"));
304       else
305         printf_unfiltered (_("Output will be logged and displayed.\n"));
306     }
307 }
308
309 /* Provide a prototype to silence -Wmissing-prototypes.  */
310 extern initialize_file_ftype _initialize_cli_logging;
311
312 void
313 _initialize_cli_logging (void)
314 {
315   static struct cmd_list_element *set_logging_cmdlist, *show_logging_cmdlist;
316
317   add_prefix_cmd ("logging", class_support, set_logging_command,
318                   _("Set logging options"), &set_logging_cmdlist,
319                   "set logging ", 0, &setlist);
320   add_prefix_cmd ("logging", class_support, show_logging_command,
321                   _("Show logging options"), &show_logging_cmdlist,
322                   "show logging ", 0, &showlist);
323   add_setshow_boolean_cmd ("overwrite", class_support, &logging_overwrite, _("\
324 Set whether logging overwrites or appends to the log file."), _("\
325 Show whether logging overwrites or appends to the log file."), _("\
326 If set, logging overrides the log file."),
327                            set_logging_overwrite,
328                            show_logging_overwrite,
329                            &set_logging_cmdlist, &show_logging_cmdlist);
330   add_setshow_boolean_cmd ("redirect", class_support, &logging_redirect, _("\
331 Set the logging output mode."), _("\
332 Show the logging output mode."), _("\
333 If redirect is off, output will go to both the screen and the log file.\n\
334 If redirect is on, output will go only to the log file."),
335                            set_logging_redirect,
336                            show_logging_redirect,
337                            &set_logging_cmdlist, &show_logging_cmdlist);
338   add_setshow_filename_cmd ("file", class_support, &logging_filename, _("\
339 Set the current logfile."), _("\
340 Show the current logfile."), _("\
341 The logfile is used when directing GDB's output."),
342                             NULL,
343                             show_logging_filename,
344                             &set_logging_cmdlist, &show_logging_cmdlist);
345   add_cmd ("on", class_support, set_logging_on,
346            _("Enable logging."), &set_logging_cmdlist);
347   add_cmd ("off", class_support, set_logging_off,
348            _("Disable logging."), &set_logging_cmdlist);
349
350   logging_filename = xstrdup ("gdb.txt");
351 }