Re: RFA: >, >>, and "tee" operators

Daniel Jacobowitz
2002-08-01T19:03:51Z

On Thu, Aug 01, 2002 at 10:24:54AM -0700, david carlton wrote:
> On Thu, 01 Aug 2002 11:05:28 -0400, Andrew Cagney
> <ac131313@ges.redhat.com> said:
>
> [ In response to my comment that the 'print -x EXPRESSION' syntax
> didn't break many cases and those case could be fixed with adding
> parentheses. ]
>
> > There is an old rule ``KISS''. At some point there are too many
> > workarounds and edge cases and they are arising too frequently.
>
> Yes, that's true. But keeping things simple has several implications
> here:
>
> 1) Option syntax should be uniform between programs.
> 2) Option syntax should be uniform within a single program.
> 3) Option syntax should be chosen so as to make it clear when
> something is an option and when something isn't.
>
> Point 1) suggests that option syntax should start with -. (Or perhaps
> with - in a Unix environment and / in other appropriate environments.)
> Point 2) suggests that all options should start the same way. Point
> 3) suggests that option syntax shouldn't start with -. (Incidentally,
> are there programming languages in which / can be a unary operator?
> Not that I'm suggesting we should seriously worry about that, I'm just
> curious.) But, as we've all noticed, these are incompatible.
>
> Daniel suggests that bending 2) to the extent that GDB does is
> acceptable. I suggest that bending 3) might be acceptable, or at any
> rate isn't clearly less acceptable than bending 2). I'm not sure
> which one you want to bend, or if you see another way out of this
> problem; you seem strongly against bending 2) or 3), but I doubt that
> bending 1) would make too many people happy either.

> Of course, this is all a bit academic now: it seems that people agree
> that, for commands that take filenames, using - to start options is
> reasonable, which answers the immediate question that started the
> thread.

I'm not sure we really reached that agreement... though I'd be happy if
we had :)

Here's the current version of this patch. It uses (and documents,
hopefully I got the texinfo right this time):
redirect [-a] [FILE [COMMAND]]
log [-a] [FILE [COMMAND]]

Andrew, would you prefer to spend some time discussing command line
syntaxen first? I'd rather put this in (I think the syntax is right,
as I've explained) and then start trying to draft a command line
overhaul for 6.0. Formally specify commands, write a less ad-hoc
command line parser and use it in calling commands, etc.

(Is syntaxen a word?)

--
Daniel Jacobowitz Carnegie Mellon University
MontaVista Software Debian GNU/Linux Developer

2002-08-01 Tom Tromey <tromey@redhat.com>
Daniel Jacobowitz <drow@mvista.com>

* cli-out.c (struct ui_out_data): Add original_stream.
(cli_redirect): New function.
(cli_ui_out_impl): Add cli_redirect.
(cli_out_new): Initialize original_stream.
* ui-out.c (default_ui_out_impl): Add NULL for redirect member.
(uo_redirect, ui_out_redirect): New.
* ui-out.h (struct ui_out_impl): Add redirect member.
(redirect_ftype): New.
(ui_out_redirect): Add prototype.
* top.c (redirect_output, log_output, handle_redirections)
(pop_output_files): New functions.
(init_main): Create "redirect" and "log" commands.

2002-08-01 Daniel Jacobowitz <drow@mvista.com>

* mi-out.c (mi_ui_out_impl): Add NULL for redirect member.

2002-08-01 Daniel Jacobowitz <drow@mvista.com>

* tui-out.c (tui_ui_out_impl): Add NULL for redirect member.

2002-08-01 Daniel Jacobowitz <drow@mvista.com>

* gdb.texinfo (Redirecting output): New chapter.

Index: cli-out.c
===================================================================
RCS file: /cvs/src/src/gdb/cli-out.c,v
retrieving revision 1.14
diff -u -p -u -r1.14 cli-out.c
--- cli-out.c 19 Mar 2002 02:51:04 -0000 1.14
+++ cli-out.c 1 Aug 2002 18:50:59 -0000
@@ -31,6 +31,7 @@
struct ui_out_data
{
struct ui_file *stream;
+ struct ui_file *original_stream;
int suppress_output;
};

@@ -63,6 +64,7 @@ static void cli_message (struct ui_out *
const char *format, va_list args);
static void cli_wrap_hint (struct ui_out *uiout, char *identstring);
static void cli_flush (struct ui_out *uiout);
+static int cli_redirect (struct ui_out *uiout, struct ui_file *outstream);

/* This is the CLI ui-out implementation functions vector */

@@ -86,6 +88,7 @@ static struct ui_out_impl cli_ui_out_imp
cli_message,
cli_wrap_hint,
cli_flush,
+ cli_redirect,
0, /* Does not need MI hacks (i.e. needs CLI hacks). */
};

@@ -323,6 +326,24 @@ cli_flush (struct ui_out *uiout)
gdb_flush (data->stream);
}

+int
+cli_redirect (struct ui_out *uiout, struct ui_file *outstream)
+{
+ struct ui_out_data *data = ui_out_data (uiout);
+ if (outstream != NULL)
+ {
+ data->original_stream = data->stream;
+ data->stream = outstream;
+ }
+ else if (data->original_stream != NULL)
+ {
+ data->stream = data->original_stream;
+ data->original_stream = NULL;
+ }
+
+ return 0;
+}
+
/* local functions */

/* Like cli_field_fmt, but takes a variable number of args
@@ -361,6 +382,7 @@ cli_out_new (struct ui_file *stream)

struct ui_out_data *data = XMALLOC (struct ui_out_data);
data->stream = stream;
+ data->original_stream = NULL;
data->suppress_output = 0;
return ui_out_new (&cli_ui_out_impl, data, flags);
}
Index: top.c
===================================================================
RCS file: /cvs/src/src/gdb/top.c,v
retrieving revision 1.65
diff -u -p -u -r1.65 top.c
--- top.c 24 Jul 2002 17:58:46 -0000 1.65
+++ top.c 1 Aug 2002 18:50:59 -0000
@@ -1730,6 +1730,191 @@ dont_repeat_command (char *ignored, int
necessarily reading from stdin. */
}
\f
+/* Functions and variables for gdb output redirection. */
+
+/* These hold the pushed copies of the gdb output files.
+ If NULL then nothing has yet been pushed. */
+static struct ui_file *saved_stdout;
+static struct ui_file *saved_stderr;
+static struct ui_file *saved_stdlog;
+static struct ui_file *saved_stdtarg;
+static char *saved_filename;
+
+/* If we've pushed output files, close them and pop them. */
+static void
+pop_output_files ()
+{
+ if (saved_stdout != NULL)
+ {
+ xfree (saved_filename);
+ saved_filename = NULL;
+
+ /* Only delete one of the files -- they are all set to the same
+ value. */
+ ui_file_delete (gdb_stdout);
+ gdb_stdout = saved_stdout;
+ gdb_stderr = saved_stderr;
+ gdb_stdlog = saved_stdlog;
+ gdb_stdtarg = saved_stdtarg;
+ saved_stdout = NULL;
+ saved_stderr = NULL;
+ saved_stdlog = NULL;
+ saved_stdtarg = NULL;
+
+ ui_out_redirect (uiout, NULL);
+ }
+}
+
+/* This is a helper for the `redirect' and `log' redirection commands. */
+static void
+handle_redirections (char *command, char *filename, char *mode, int tee,
+ int from_tty)
+{
+ struct ui_file *output;
+ struct ui_file *tmp_stdout, *tmp_stderr, *tmp_stdlog, *tmp_stdtarg;
+ char *cmd = NULL;
+
+ if (filename == NULL)
+ return;
+
+ if (*filename)
+ {
+ /* Skip whitespace. */
+ while (*filename && isspace (*filename))
+ filename++;
+
+ /* Skip the filename. */
+ cmd = filename;
+ while (*cmd && !isspace (*cmd))
+ cmd ++;
+ if (*cmd)
+ *(cmd++) = 0;
+ while (*cmd && isspace (*cmd))
+ cmd ++;
+
+ if (!*cmd)
+ cmd = NULL;
+ }
+
+ if (cmd == NULL)
+ {
+ char *tmp = NULL;
+ if (from_tty && saved_filename != NULL)
+ tmp = xstrdup (saved_filename);
+
+ pop_output_files ();
+
+ if (tmp != NULL)
+ {
+ fprintf_unfiltered (saved_stdout, "Done redirecting to %s.\n",
+ saved_filename);
+ xfree (tmp);
+ }
+ }
+
+ if (*filename == 0)
+ return;
+
+ output = gdb_fopen (filename, mode);
+ if (output == NULL)
+ perror_with_name (command);
+
+ /* Redirects everything to gdb_stdout while this is running. */
+ if (tee)
+ {
+ output = tee_file_new (gdb_stdout, 0, output, 1);
+ if (output == NULL)
+ perror_with_name (command);
+ if (from_tty && cmd == NULL)
+ fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n", filename);
+ }
+ else if (from_tty && cmd == NULL)
+ fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n", filename);
+
+ if (cmd == NULL)
+ {
+ saved_filename = xstrdup (filename);
+ saved_stdout = gdb_stdout;
+ saved_stderr = gdb_stderr;
+ saved_stdlog = gdb_stdlog;
+ saved_stdtarg = gdb_stdtarg;
+ }
+ else
+ {
+ tmp_stdout = gdb_stdout;
+ tmp_stderr = gdb_stderr;
+ tmp_stdlog = gdb_stdlog;
+ tmp_stdtarg = gdb_stdtarg;
+ }
+
+ gdb_stdout = output;
+ gdb_stderr = output;
+ gdb_stdlog = output;
+ gdb_stdtarg = output;
+ if (ui_out_redirect (uiout, gdb_stdout) < 0)
+ warning ("Current output protocol does not support redirection");
+
+ if (cmd != NULL)
+ {
+ catch_command_errors (execute_command, cmd,
+ from_tty, RETURN_MASK_ALL);
+
+ /* Only delete one of the files -- they are all set to the same
+ value. */
+ ui_file_delete (gdb_stdout);
+
+ gdb_stdout = tmp_stdout;
+ gdb_stderr = tmp_stderr;
+ gdb_stdlog = tmp_stdlog;
+ gdb_stdtarg = tmp_stdtarg;
+ if (saved_filename)
+ ui_out_redirect (uiout, gdb_stdout);
+ else
+ ui_out_redirect (uiout, NULL);
+ }
+}
+
+/* Redirect output to a file. */
+static void
+redirect_output (char *args, int from_tty)
+{
+ if (args)
+ {
+ /* Check for "-a". */
+ while (*args && isspace (*args))
+ ++args;
+
+ if (args[0] == '-' && args[1] == 'a' && isspace (args[2]))
+ {
+ handle_redirections ("redirect", args + 3, "a", 0, from_tty);
+ return;
+ }
+ }
+
+ handle_redirections ("redirect", args, "w", 0, from_tty);
+}
+
+/* Redirect output by writing to a file and writing to the screen. */
+static void
+log_output (char *args, int from_tty)
+{
+ if (args)
+ {
+ /* Check for "-a". */
+ while (*args && isspace (*args))
+ ++args;
+
+ if (args[0] == '-' && args[1] == 'a' && isspace (args[2]))
+ {
+ handle_redirections ("log", args + 3, "a", 1, from_tty);
+ return;
+ }
+ }
+
+ handle_redirections ("log", args, "w", 1, from_tty);
+}
+
+\f
/* Functions to manipulate command line editing control variables. */

/* Number of commands to print in each call to show_commands. */
@@ -2063,6 +2248,15 @@ ie. the number of previous commands to k
Use \"on\" to enable the notification, and \"off\" to disable it.", &setlist),
&showlist);
}
+
+ add_com ("redirect", no_class, redirect_output,
+ "Redirect further gdb output to a file.\n\
+If \"-a\" is specified append to the file; otherwise overwrite it.\n\
+If no filename is given, any previous redirection is stopped.");
+ add_com ("log", no_class, log_output,
+ "Send further gdb output to both the terminal and a file.\n\
+If \"-a\" is specified append to the file; otherwise overwrite it.\n\
+If no filename is given, any previous redirection is stopped.");
}

void
Index: ui-out.c
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 ui-out.c
--- ui-out.c 27 Jul 2002 01:54:15 -0000 1.23
+++ ui-out.c 1 Aug 2002 18:50:59 -0000
@@ -206,6 +206,7 @@ struct ui_out_impl default_ui_out_impl =
default_message,
default_wrap_hint,
default_flush,
+ NULL,
0, /* Does not need MI hacks. */
};

@@ -254,6 +255,7 @@ static void uo_message (struct ui_out *u
const char *format, va_list args);
static void uo_wrap_hint (struct ui_out *uiout, char *identstring);
static void uo_flush (struct ui_out *uiout);
+static int uo_redirect (struct ui_out *uiout, struct ui_file *outstream);

/* Prototypes for local functions */

@@ -639,6 +641,12 @@ ui_out_flush (struct ui_out *uiout)
uo_flush (uiout);
}

+int
+ui_out_redirect (struct ui_out *uiout, struct ui_file *outstream)
+{
+ uo_redirect (uiout, outstream);
+}
+
/* set the flags specified by the mask given */
int
ui_out_set_flags (struct ui_out *uiout, int mask)
@@ -980,6 +988,14 @@ uo_flush (struct ui_out *uiout)
if (!uiout->impl->flush)
return;
uiout->impl->flush (uiout);
+}
+
+int
+uo_redirect (struct ui_out *uiout, struct ui_file *outstream)
+{
+ if (!uiout->impl->redirect)
+ return -1;
+ uiout->impl->redirect (uiout, outstream);
}

/* local functions */
Index: ui-out.h
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.h,v
retrieving revision 1.15
diff -u -p -u -r1.15 ui-out.h
--- ui-out.h 6 Jul 2001 03:53:11 -0000 1.15
+++ ui-out.h 1 Aug 2002 18:50:59 -0000
@@ -237,6 +237,8 @@ typedef void (message_ftype) (struct ui_
const char *format, va_list args);
typedef void (wrap_hint_ftype) (struct ui_out * uiout, char *identstring);
typedef void (flush_ftype) (struct ui_out * uiout);
+typedef int (redirect_ftype) (struct ui_out * uiout,
+ struct ui_file * outstream);

/* ui-out-impl */

@@ -260,6 +262,7 @@ struct ui_out_impl
message_ftype *message;
wrap_hint_ftype *wrap_hint;
flush_ftype *flush;
+ redirect_ftype *redirect;
int is_mi_like_p;
};

@@ -271,5 +274,9 @@ extern struct ui_out_data *ui_out_data (
extern struct ui_out *ui_out_new (struct ui_out_impl *impl,
struct ui_out_data *data,
int flags);
+
+/* Redirect the ouptut of a ui_out object temporarily. */
+
+extern int ui_out_redirect (struct ui_out *uiout, struct ui_file *outstream);

#endif /* UI_OUT_H */
Index: cli/cli-decode.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-decode.c,v
retrieving revision 1.28
diff -u -p -u -r1.28 cli-decode.c
--- cli/cli-decode.c 30 Jul 2002 13:45:14 -0000 1.28
+++ cli/cli-decode.c 1 Aug 2002 18:51:00 -0000
@@ -924,7 +924,7 @@ lookup_cmd_1 (char **text, struct cmd_li
so that "set args_foo()" doesn't get interpreted as
"set args _foo()". */
for (p = *text;
- *p && (isalnum (*p) || *p == '-' || *p == '_' ||
+ *p && (isalnum (*p) || *p == '-' || *p == '_' ||
(tui_version &&
(*p == '+' || *p == '<' || *p == '>' || *p == '$')) ||
(xdb_commands && (*p == '!' || *p == '/' || *p == '?')));
Index: mi/mi-out.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-out.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 mi-out.c
--- mi/mi-out.c 19 Mar 2002 02:51:08 -0000 1.23
+++ mi/mi-out.c 1 Aug 2002 18:51:04 -0000
@@ -85,6 +85,7 @@ struct ui_out_impl mi_ui_out_impl =
mi_message,
mi_wrap_hint,
mi_flush,
+ NULL,
1, /* Needs MI hacks. */
};

Index: tui/tui-out.c
===================================================================
RCS file: /cvs/src/src/gdb/tui/tui-out.c,v
retrieving revision 1.2
diff -u -p -u -r1.2 tui-out.c
--- tui/tui-out.c 19 Mar 2002 02:51:09 -0000 1.2
+++ tui/tui-out.c 1 Aug 2002 18:51:04 -0000
@@ -88,6 +88,7 @@ static struct ui_out_impl tui_ui_out_imp
tui_message,
tui_wrap_hint,
tui_flush,
+ NULL,
0, /* Does not need MI hacks (i.e. needs CLI hacks). */
};

Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.105
diff -u -p -r1.105 gdb.texinfo
--- doc/gdb.texinfo 24 Jul 2002 23:51:36 -0000 1.105
+++ doc/gdb.texinfo 1 Aug 2002 18:58:16 -0000
@@ -752,6 +752,7 @@ type @kbd{quit} or @kbd{C-d} to exit.
* Invoking GDB:: How to start @value{GDBN}
* Quitting GDB:: How to quit @value{GDBN}
* Shell Commands:: How to use shell commands inside @value{GDBN}
+* Redirecting output:: How to redirect @value{GDBN}'s output to files
@end menu

@node Invoking GDB
@@ -1201,6 +1202,26 @@ You do not have to use the @code{shell}
Execute the @code{make} program with the specified
arguments. This is equivalent to @samp{shell make @var{make-args}}.
@end table
+
+@node Redirecting output
+@section Redirecting output
+
+You may want to save the output of @value{GDBN} commands to a file.
+There are three commands to control @value{GDBN}'s logging.
+
+@table @code
+@kindex redirect
+@item redirect [-a] [@var{file} [@var{command}]]
+Redirect all output to @var{file}.
+@kindex log
+@item log [-a] [@var{file} [@var{command}]]
+Copy output to both the screen and @var{file}.
+@end table
+
+Both @code{redirect} and @code{log} default to overwriting the log file,
+unless @code{-a} is specified. You may start redirection by specifying
+just @var{file}, end redirection by not specifying @var{file}, and redirect
+the output of only one command by specifying @var{command} after the filename.

@node Commands
@chapter @value{GDBN} Commands