Fix "-gdb-set logging redirect on" crash
[binutils-gdb.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 maybe_warn_already_logging ()
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 set_logging_overwrite (char *args, int from_tty, struct cmd_list_element *c)
59 {
60 maybe_warn_already_logging ();
61 }
62
63 static void
64 show_logging_overwrite (struct ui_file *file, int from_tty,
65 struct cmd_list_element *c, const char *value)
66 {
67 fprintf_filtered (file,
68 _("Whether logging overwrites or "
69 "appends to the log file is %s.\n"),
70 value);
71 }
72
73 /* Value as configured by the user. */
74 static int logging_redirect;
75
76 static void
77 set_logging_redirect (char *args, int from_tty, struct cmd_list_element *c)
78 {
79 maybe_warn_already_logging ();
80 }
81
82 static void
83 show_logging_redirect (struct ui_file *file, int from_tty,
84 struct cmd_list_element *c, const char *value)
85 {
86 fprintf_filtered (file, _("The logging output mode is %s.\n"), value);
87 }
88
89 /* If we've pushed output files, close them and pop them. */
90 static void
91 pop_output_files (void)
92 {
93 if (current_interp_set_logging (0, NULL, NULL) == 0)
94 {
95 /* Only delete one of the files -- they are all set to the same
96 value. */
97 delete gdb_stdout;
98
99 gdb_stdout = saved_output.out;
100 gdb_stderr = saved_output.err;
101 gdb_stdlog = saved_output.log;
102 gdb_stdtarg = saved_output.targ;
103 gdb_stdtargerr = saved_output.targerr;
104 }
105
106 saved_output.out = NULL;
107 saved_output.err = NULL;
108 saved_output.log = NULL;
109 saved_output.targ = NULL;
110 saved_output.targerr = NULL;
111
112 /* Stay consistent with handle_redirections. */
113 if (!current_uiout->is_mi_like_p ())
114 current_uiout->redirect (NULL);
115 }
116
117 /* This is a helper for the `set logging' command. */
118 static void
119 handle_redirections (int from_tty)
120 {
121 ui_file_up output;
122 ui_file_up no_redirect_file;
123
124 if (saved_filename != NULL)
125 {
126 fprintf_unfiltered (gdb_stdout, "Already logging to %s.\n",
127 saved_filename);
128 return;
129 }
130
131 stdio_file_up log (new stdio_file ());
132 if (!log->open (logging_filename, logging_overwrite ? "w" : "a"))
133 perror_with_name (_("set logging"));
134
135 /* Redirects everything to gdb_stdout while this is running. */
136 if (!logging_redirect)
137 {
138 no_redirect_file = std::move (log);
139 output.reset (new tee_file (gdb_stdout, 0, no_redirect_file.get (), 0));
140
141 if (from_tty)
142 fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n",
143 logging_filename);
144 }
145 else
146 {
147 output = std::move (log);
148
149 if (from_tty)
150 fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n",
151 logging_filename);
152 }
153
154 saved_filename = xstrdup (logging_filename);
155 saved_output.out = gdb_stdout;
156 saved_output.err = gdb_stderr;
157 saved_output.log = gdb_stdlog;
158 saved_output.targ = gdb_stdtarg;
159 saved_output.targerr = gdb_stdtargerr;
160
161 /* Let the interpreter do anything it needs. */
162 if (current_interp_set_logging (1, output.get (),
163 no_redirect_file.get ()) == 0)
164 {
165 gdb_stdout = output.get ();
166 gdb_stdlog = output.get ();
167 gdb_stderr = output.get ();
168 gdb_stdtarg = output.get ();
169 gdb_stdtargerr = output.get ();
170 }
171
172 output.release ();
173 no_redirect_file.release ();
174
175 /* Don't do the redirect for MI, it confuses MI's ui-out scheme. */
176 if (!current_uiout->is_mi_like_p ())
177 current_uiout->redirect (gdb_stdout);
178 }
179
180 static void
181 set_logging_on (char *args, int from_tty)
182 {
183 char *rest = args;
184
185 if (rest && *rest)
186 {
187 xfree (logging_filename);
188 logging_filename = xstrdup (rest);
189 }
190 handle_redirections (from_tty);
191 }
192
193 static void
194 set_logging_off (char *args, int from_tty)
195 {
196 if (saved_filename == NULL)
197 return;
198
199 pop_output_files ();
200 if (from_tty)
201 fprintf_unfiltered (gdb_stdout, "Done logging to %s.\n", saved_filename);
202 xfree (saved_filename);
203 saved_filename = NULL;
204 }
205
206 static void
207 set_logging_command (char *args, int from_tty)
208 {
209 printf_unfiltered (_("\"set logging\" lets you log output to a file.\n"
210 "Usage: set logging on [FILENAME]\n"
211 " set logging off\n"
212 " set logging file FILENAME\n"
213 " set logging overwrite [on|off]\n"
214 " set logging redirect [on|off]\n"));
215 }
216
217 static void
218 show_logging_command (char *args, int from_tty)
219 {
220 if (saved_filename)
221 printf_unfiltered (_("Currently logging to \"%s\".\n"), saved_filename);
222 if (saved_filename == NULL
223 || strcmp (logging_filename, saved_filename) != 0)
224 printf_unfiltered (_("Future logs will be written to %s.\n"),
225 logging_filename);
226
227 if (logging_overwrite)
228 printf_unfiltered (_("Logs will overwrite the log file.\n"));
229 else
230 printf_unfiltered (_("Logs will be appended to the log file.\n"));
231
232 if (logging_redirect)
233 printf_unfiltered (_("Output will be sent only to the log file.\n"));
234 else
235 printf_unfiltered (_("Output will be logged and displayed.\n"));
236 }
237
238 /* Provide a prototype to silence -Wmissing-prototypes. */
239 extern initialize_file_ftype _initialize_cli_logging;
240
241 void
242 _initialize_cli_logging (void)
243 {
244 static struct cmd_list_element *set_logging_cmdlist, *show_logging_cmdlist;
245
246 add_prefix_cmd ("logging", class_support, set_logging_command,
247 _("Set logging options"), &set_logging_cmdlist,
248 "set logging ", 0, &setlist);
249 add_prefix_cmd ("logging", class_support, show_logging_command,
250 _("Show logging options"), &show_logging_cmdlist,
251 "show logging ", 0, &showlist);
252 add_setshow_boolean_cmd ("overwrite", class_support, &logging_overwrite, _("\
253 Set whether logging overwrites or appends to the log file."), _("\
254 Show whether logging overwrites or appends to the log file."), _("\
255 If set, logging overrides the log file."),
256 set_logging_overwrite,
257 show_logging_overwrite,
258 &set_logging_cmdlist, &show_logging_cmdlist);
259 add_setshow_boolean_cmd ("redirect", class_support, &logging_redirect, _("\
260 Set the logging output mode."), _("\
261 Show the logging output mode."), _("\
262 If redirect is off, output will go to both the screen and the log file.\n\
263 If redirect is on, output will go only to the log file."),
264 set_logging_redirect,
265 show_logging_redirect,
266 &set_logging_cmdlist, &show_logging_cmdlist);
267 add_setshow_filename_cmd ("file", class_support, &logging_filename, _("\
268 Set the current logfile."), _("\
269 Show the current logfile."), _("\
270 The logfile is used when directing GDB's output."),
271 NULL,
272 show_logging_filename,
273 &set_logging_cmdlist, &show_logging_cmdlist);
274 add_cmd ("on", class_support, set_logging_on,
275 _("Enable logging."), &set_logging_cmdlist);
276 add_cmd ("off", class_support, set_logging_off,
277 _("Disable logging."), &set_logging_cmdlist);
278
279 logging_filename = xstrdup ("gdb.txt");
280 }