From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26613 invoked by alias); 23 Jul 2002 18:39:53 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 26602 invoked from network); 23 Jul 2002 18:39:52 -0000 Received: from unknown (HELO crack.them.org) (65.125.64.184) by sources.redhat.com with SMTP; 23 Jul 2002 18:39:52 -0000 Received: from dsl254-114-118.nyc1.dsl.speakeasy.net ([216.254.114.118] helo=nevyn.them.org ident=mail) by crack.them.org with asmtp (Exim 3.12 #1 (Debian)) id 17X4Zc-0002nE-00; Tue, 23 Jul 2002 13:39:53 -0500 Received: from drow by nevyn.them.org with local (Exim 3.35 #1 (Debian)) id 17X4Zg-0007RD-00; Tue, 23 Jul 2002 14:39:56 -0400 Date: Tue, 23 Jul 2002 11:51:00 -0000 From: Daniel Jacobowitz To: gdb-patches@sources.redhat.com, tromey@redhat.com Subject: RFA: >, >>, and "tee" operators Message-ID: <20020723183956.GA28558@nevyn.them.org> Mail-Followup-To: gdb-patches@sources.redhat.com, tromey@redhat.com Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.1i X-SW-Source: 2002-07/txt/msg00459.txt.bz2 Here we go. They only work quite right for the CLI; they sort-of work for other front-ends, and print a warning to that effect. Documentation included. These are pretty much how Tom originally did them: > file >> file and my additions: tee file tee -a file Any of the commands without a filename will end redirection. Comments? OK to apply? Design too ugly? -- Daniel Jacobowitz Carnegie Mellon University MontaVista Software Debian GNU/Linux Developer 2002-07-23 Tom Tromey Daniel Jacobowitz * 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-file.c (struct tee_file, tee_file_new, tee_file_delete) (tee_file_flush, tee_file_write, tee_file_fputs) (tee_file_isatty): New. * ui-file.h (tee_file_new): Add prototype. * 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. * cli/cli-decode.c (lookup_cmd): Allow `>' in command name. (lookup_cmd_1): Likewise. * top.c (redirect_output): New function. (append_output): Likewise. (handle_redirections): Likewise. (pop_output_files): Likewise. (init_main): Create ">", ">>", and "tee" commands. 2002-07-23 Daniel Jacobowitz * mi-out.c (mi_ui_out_impl): Add NULL for redirect member. 2002-07-23 Daniel Jacobowitz * tui-out.c (tui_ui_out_impl): Add NULL for redirect member. 2002-07-23 Daniel Jacobowitz * 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 -r1.14 cli-out.c --- cli-out.c 19 Mar 2002 02:51:04 -0000 1.14 +++ cli-out.c 23 Jul 2002 18:26:19 -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.64 diff -u -p -r1.64 top.c --- top.c 11 Jul 2002 13:50:49 -0000 1.64 +++ top.c 23 Jul 2002 18:26:19 -0000 @@ -1707,6 +1707,133 @@ dont_repeat_command (char *ignored, int necessarily reading from stdin. */ } +/* 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 `>', `>>', and `tee' redirection commands. */ +static void +handle_redirections (char *command, char *filename, char *mode, int tee) +{ + struct ui_file *output; + + if (saved_filename != NULL) + fprintf_unfiltered (saved_stdout, "Done redirecting to %s.\n", saved_filename); + + pop_output_files (); + + /* Skip whitespace. */ + if (filename == NULL) + return; + + while (*filename && isspace (*filename)) + ++filename; + + if (*filename == 0) + return; + + /* It would be nice if the syntax was: + > FILE [COMMAND] + .. if COMMAND is specified then it (and only it) is run with + redirections. This means inventing a quoting syntax for the file + name though. */ + 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); + fprintf_unfiltered (gdb_stdout, "Copying output to %s.\n", filename); + } + else + fprintf_unfiltered (gdb_stdout, "Redirecting output to %s.\n", filename); + + saved_filename = xstrdup (filename); + saved_stdout = gdb_stdout; + saved_stderr = gdb_stderr; + saved_stdlog = gdb_stdlog; + saved_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"); +} + +/* Redirect output by overwriting a file. */ +/* ARGSUSED */ +static void +redirect_output (char *args, int from_tty) +{ + handle_redirections (">", args, "w", 0); +} + +/* Redirect output by appending to a file. */ +/* ARGSUSED */ +static void +append_output (char *args, int from_tty) +{ + handle_redirections (">>", args, "a", 0); +} + +/* Redirect output by writing to a file and writing to the screen. */ +/* ARGSUSED */ +static void +tee_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 ("tee", args + 3, "a", 1); + return; + } + } + + handle_redirections ("tee", args, "w", 1); +} + + /* Functions to manipulate command line editing control variables. */ /* Number of commands to print in each call to show_commands. */ @@ -2040,6 +2167,17 @@ ie. the number of previous commands to k Use \"on\" to enable the notification, and \"off\" to disable it.", &setlist), &showlist); } + + add_com (">", no_class, redirect_output, + "Redirect further gdb output to a file.\n\ +If no filename is given, any previous redirection is stopped."); + add_com (">>", no_class, append_output, + "Append further gdb output to a file.\n\ +If no filename is given, any previous redirection is stopped."); + add_com ("tee", no_class, tee_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-file.c =================================================================== RCS file: /cvs/src/src/gdb/ui-file.c,v retrieving revision 1.8 diff -u -p -r1.8 ui-file.c --- ui-file.c 19 Mar 2002 02:51:07 -0000 1.8 +++ ui-file.c 23 Jul 2002 18:26:19 -0000 @@ -483,3 +483,97 @@ gdb_fopen (char *name, char *mode) return NULL; return stdio_file_new (f, 1); } + +/* ``struct ui_file'' implementation that maps onto two ui-file objects. */ + +static ui_file_write_ftype tee_file_write; +static ui_file_fputs_ftype tee_file_fputs; +static ui_file_isatty_ftype tee_file_isatty; +static ui_file_delete_ftype tee_file_delete; +static ui_file_flush_ftype tee_file_flush; + +static int tee_file_magic; + +struct tee_file + { + int *magic; + struct ui_file *one, *two; + int close_one, close_two; + }; + +struct ui_file * +tee_file_new (struct ui_file *one, int close_one, + struct ui_file *two, int close_two) +{ + struct ui_file *ui_file = ui_file_new (); + struct tee_file *tee = xmalloc (sizeof (struct tee_file)); + tee->magic = &tee_file_magic; + tee->one = one; + tee->two = two; + tee->close_one = close_one; + tee->close_two = close_two; + set_ui_file_data (ui_file, tee, tee_file_delete); + set_ui_file_flush (ui_file, tee_file_flush); + set_ui_file_write (ui_file, tee_file_write); + set_ui_file_fputs (ui_file, tee_file_fputs); + set_ui_file_isatty (ui_file, tee_file_isatty); + return ui_file; +} + +static void +tee_file_delete (struct ui_file *file) +{ + struct tee_file *tee = ui_file_data (file); + if (tee->magic != &tee_file_magic) + internal_error (__FILE__, __LINE__, + "tee_file_delete: bad magic number"); + if (tee->close_one) + ui_file_delete (tee->one); + if (tee->close_two) + ui_file_delete (tee->two); + + xfree (tee); +} + +static void +tee_file_flush (struct ui_file *file) +{ + struct tee_file *tee = ui_file_data (file); + if (tee->magic != &tee_file_magic) + internal_error (__FILE__, __LINE__, + "tee_file_flush: bad magic number"); + tee->one->to_flush (tee->one); + tee->two->to_flush (tee->two); +} + +static void +tee_file_write (struct ui_file *file, const char *buf, long length_buf) +{ + struct tee_file *tee = ui_file_data (file); + if (tee->magic != &tee_file_magic) + internal_error (__FILE__, __LINE__, + "tee_file_write: bad magic number"); + ui_file_write (tee->one, buf, length_buf); + ui_file_write (tee->two, buf, length_buf); +} + +static void +tee_file_fputs (const char *linebuffer, struct ui_file *file) +{ + struct tee_file *tee = ui_file_data (file); + if (tee->magic != &tee_file_magic) + internal_error (__FILE__, __LINE__, + "tee_file_fputs: bad magic number"); + tee->one->to_fputs (linebuffer, tee->one); + tee->two->to_fputs (linebuffer, tee->two); +} + +static int +tee_file_isatty (struct ui_file *file) +{ + struct tee_file *tee = ui_file_data (file); + if (tee->magic != &tee_file_magic) + internal_error (__FILE__, __LINE__, + "tee_file_isatty: bad magic number"); + return (0); +} Index: ui-file.h =================================================================== RCS file: /cvs/src/src/gdb/ui-file.h,v retrieving revision 1.2 diff -u -p -r1.2 ui-file.h --- ui-file.h 6 Mar 2001 08:21:17 -0000 1.2 +++ ui-file.h 23 Jul 2002 18:26:19 -0000 @@ -90,4 +90,11 @@ extern struct ui_file *stdio_fileopen (F /* Open NAME returning an STDIO based UI_FILE. */ extern struct ui_file *gdb_fopen (char *name, char *mode); +/* Create a file which writes to both ONE and TWO. CLOSE_ONE + and CLOSE_TWO indicate whether the original files should be + closed when the new file is closed. */ +struct ui_file *tee_file_new (struct ui_file *one, + int close_one, + struct ui_file *two, + int close_two); #endif Index: ui-out.c =================================================================== RCS file: /cvs/src/src/gdb/ui-out.c,v retrieving revision 1.22 diff -u -p -r1.22 ui-out.c --- ui-out.c 5 May 2002 03:17:21 -0000 1.22 +++ ui-out.c 23 Jul 2002 18:26:20 -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_out *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 -r1.15 ui-out.h --- ui-out.h 6 Jul 2001 03:53:11 -0000 1.15 +++ ui-out.h 23 Jul 2002 18:26:20 -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; }; Index: cli/cli-decode.c =================================================================== RCS file: /cvs/src/src/gdb/cli/cli-decode.c,v retrieving revision 1.27 diff -u -p -r1.27 cli-decode.c --- cli/cli-decode.c 3 Jul 2002 17:35:21 -0000 1.27 +++ cli/cli-decode.c 23 Jul 2002 18:26:20 -0000 @@ -923,7 +923,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 == '_' || *p == '>' || (tui_version && (*p == '+' || *p == '<' || *p == '>' || *p == '$')) || (xdb_commands && (*p == '!' || *p == '/' || *p == '?'))); @@ -1086,7 +1086,7 @@ lookup_cmd (char **line, struct cmd_list { char *p = *line, *q; - while (isalnum (*p) || *p == '-') + while (isalnum (*p) || *p == '-' || *p == '>') p++; q = (char *) alloca (p - *line + 1); Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.104 diff -u -p -r1.104 gdb.texinfo --- doc/gdb.texinfo 9 Jul 2002 15:59:18 -0000 1.104 +++ doc/gdb.texinfo 23 Jul 2002 18:26:24 -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,31 @@ 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 > +@cindex redirect +@item > @var{file} +Redirect all output to @var{file}, overwriting it. +@kindex >> +@cindex append +@item >> @var{file} +Redirect all output to @var{file}, appending to it. +@kindex tee +@cindex tee +@item tee [-a] @var{file} +Copy output to both the screen and @var{file}, overwriting it unless -a +(append) is specified. +@end table + +To end the redirection and return to normal output, just use any of the +redirection commands without specifying a filename. @node Commands @chapter @value{GDBN} Commands Index: mi/mi-out.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-out.c,v retrieving revision 1.23 diff -u -p -r1.23 mi-out.c --- mi/mi-out.c 19 Mar 2002 02:51:08 -0000 1.23 +++ mi/mi-out.c 23 Jul 2002 18:26:25 -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 -r1.2 tui-out.c --- tui/tui-out.c 19 Mar 2002 02:51:09 -0000 1.2 +++ tui/tui-out.c 23 Jul 2002 18:26:25 -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). */ };