* remote.c (remote_cmdlist): New variable.
authorDaniel Jacobowitz <drow@false.org>
Fri, 30 Nov 2007 21:50:19 +0000 (21:50 +0000)
committerDaniel Jacobowitz <drow@false.org>
Fri, 30 Nov 2007 21:50:19 +0000 (21:50 +0000)
(PACKET_vFile_open, PACKET_vFile_pread, PACKET_vFile_pwrite)
(PACKET_vFile_close, PACKET_vFile_unlink): New constants.
(remote_buffer_add_string, remote_buffer_add_bytes)
(remote_buffer_add_int, remote_hostio_parse_result)
(remote_hostio_send_command, remote_hostio_open, remote_hostio_pwrite)
(remote_hostio_pread, remote_hostio_close, remote_hostio_unlink)
(remote_fileio_errno_to_host, remote_hostio_error, fclose_cleanup)
(remote_hostio_close_cleanup, remote_file_put, remote_file_get)
(remote_file_delete, remote_put_command, remote_get_command)
(remote_delete_command, remote_command): New functions.
(_initialize_remote): Register new packets and commands.
* Makefile.in (gdb_fileio_h): New variable.
(remote.o): Update.
(SUBDIR_MI_OBS): Add mi-cmd-target.o.
(SUBDIR_MI_SRCS): Add mi/mi-cmd-target.c.
(mi-cmd-target.o): New rule.
* mi/mi-cmd-target.c: New file.
* mi/mi-cmds.c (mi_cmds): Add target-file-delete, target-file-get,
and target-file-put.
* mi/mi-cmds.h (mi_cmd_target_file_get, mi_cmd_target_file_put)
(mi_cmd_target_file_delete): Declare.
* remote.h (remote_file_put, remote_file_get, remote_file_delete):
Declare.
* NEWS: Describe new file transfer support.

* gdb.texinfo (Debugging Programs with Multiple Processes): Correct
formatting.
(Remote Debugging): Add File Transfer section.
(Remote Configuration): Document Host I/O packets.
(GDB/MI): Add GDB/MI File Transfer Commands section.
(Remote Protocol): Add Host I/O Packets section.
(Packets): Add vFile.

* Makefile.in (OBS): Add hostio.o.
(hostio.o): New rule.
* server.h (handle_vFile): Declare.
* hostio.c: New file.
* server.c (handle_v_requests): Take packet_len and new_packet_len
for binary packets.  Call handle_vFile.
(main): Update call to handle_v_requests.

* gdb.server/file-transfer.exp, gdb.server/transfer.txt,
gdb.mi/mi-file-transfer.exp: New.

19 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/Makefile.in
gdb/gdbserver/hostio.c [new file with mode: 0644]
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/mi/mi-cmd-target.c [new file with mode: 0644]
gdb/mi/mi-cmds.c
gdb/mi/mi-cmds.h
gdb/remote.c
gdb/remote.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.mi/mi-file-transfer.exp [new file with mode: 0644]
gdb/testsuite/gdb.server/file-transfer.exp [new file with mode: 0644]
gdb/testsuite/gdb.server/transfer.txt [new file with mode: 0644]

index 4850791..20a73d6 100644 (file)
@@ -1,3 +1,31 @@
+2007-11-30  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * remote.c (remote_cmdlist): New variable.
+       (PACKET_vFile_open, PACKET_vFile_pread, PACKET_vFile_pwrite)
+       (PACKET_vFile_close, PACKET_vFile_unlink): New constants.
+       (remote_buffer_add_string, remote_buffer_add_bytes)
+       (remote_buffer_add_int, remote_hostio_parse_result)
+       (remote_hostio_send_command, remote_hostio_open, remote_hostio_pwrite)
+       (remote_hostio_pread, remote_hostio_close, remote_hostio_unlink)
+       (remote_fileio_errno_to_host, remote_hostio_error, fclose_cleanup)
+       (remote_hostio_close_cleanup, remote_file_put, remote_file_get)
+       (remote_file_delete, remote_put_command, remote_get_command)
+       (remote_delete_command, remote_command): New functions.
+       (_initialize_remote): Register new packets and commands.
+       * Makefile.in (gdb_fileio_h): New variable.
+       (remote.o): Update.
+       (SUBDIR_MI_OBS): Add mi-cmd-target.o.
+       (SUBDIR_MI_SRCS): Add mi/mi-cmd-target.c.
+       (mi-cmd-target.o): New rule.
+       * mi/mi-cmd-target.c: New file.
+       * mi/mi-cmds.c (mi_cmds): Add target-file-delete, target-file-get,
+       and target-file-put.
+       * mi/mi-cmds.h (mi_cmd_target_file_get, mi_cmd_target_file_put)
+       (mi_cmd_target_file_delete): Declare.
+       * remote.h (remote_file_put, remote_file_get, remote_file_delete):
+       Declare.
+       * NEWS: Describe new file transfer support.
+
 2007-11-30  Vladimir Prus  <vladimir@codesourcery.com>
 
        * infrun.c (handle_inferior_event): Don't
index 0795114..309b2e9 100644 (file)
@@ -184,7 +184,7 @@ SUBDIR_CLI_CFLAGS=
 SUBDIR_MI_OBS = \
        mi-out.o mi-console.o \
        mi-cmds.o mi-cmd-env.o mi-cmd-var.o mi-cmd-break.o mi-cmd-stack.o \
-       mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o \
+       mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o mi-cmd-target.o \
        mi-interp.o \
        mi-main.o mi-parse.o mi-getopt.o mi-common.o
 SUBDIR_MI_SRCS = \
@@ -192,7 +192,7 @@ SUBDIR_MI_SRCS = \
        mi/mi-cmds.c mi/mi-cmd-env.c \
        mi/mi-cmd-var.c mi/mi-cmd-break.c mi/mi-cmd-stack.c \
        mi/mi-cmd-file.c mi/mi-cmd-disas.c mi/mi-symbol-cmds.c \
-       mi/mi-interp.c \
+       mi/mi-cmd-target.c mi/mi-interp.c \
        mi/mi-main.c mi/mi-parse.c mi/mi-getopt.c mi/mi-common.c
 SUBDIR_MI_DEPS =
 SUBDIR_MI_LDFLAGS=
@@ -675,6 +675,7 @@ mep_desc_h =        $(OPCODES_SRC)/mep-desc.h
 mep_opc_h =    $(OPCODES_SRC)/mep-opc.h
 sh_opc_h =     $(OPCODES_SRC)/sh-opc.h
 gdb_callback_h = $(INCLUDE_DIR)/gdb/callback.h
+gdb_fileio_h = $(INCLUDE_DIR)/gdb/fileio.h
 gdb_sim_arm_h =        $(INCLUDE_DIR)/gdb/sim-arm.h
 gdb_sim_frv_h = $(INCLUDE_DIR)/gdb/sim-frv.h
 gdb_sim_m32c_h = $(INCLUDE_DIR)/gdb/sim-m32c.h
@@ -2602,7 +2603,8 @@ remote.o: remote.c $(defs_h) $(gdb_string_h) $(inferior_h) $(bfd_h) \
        $(gdb_stabs_h) $(gdbthread_h) $(remote_h) $(regcache_h) $(value_h) \
        $(gdb_assert_h) $(event_loop_h) $(event_top_h) $(inf_loop_h) \
        $(serial_h) $(gdbcore_h) $(remote_fileio_h) $(solib_h) $(observer_h) \
-       $(cli_decode_h) $(cli_setshow_h) $(memory_map_h) $(target_descriptions_h)
+       $(cli_decode_h) $(cli_setshow_h) $(memory_map_h) \
+       $(target_descriptions_h) $(gdb_fileio_h)
 remote-fileio.o: remote-fileio.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) \
        $(remote_h) $(gdb_fileio_h) $(gdb_wait_h) $(gdb_stat_h) \
        $(exceptions_h) $(remote_fileio_h)
@@ -3186,6 +3188,9 @@ mi-cmd-stack.o: $(srcdir)/mi/mi-cmd-stack.c $(defs_h) $(target_h) $(frame_h) \
        $(value_h) $(mi_cmds_h) $(ui_out_h) $(symtab_h) $(block_h) \
        $(stack_h) $(dictionary_h) $(gdb_string_h)
        $(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-stack.c
+mi-cmd-target.o: $(srcdir)/mi/mi-cmd-target.c $(defs_h) $(mi_cmds_h) \
+       $(mi_getopt_h) $(remote_h)
+       $(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-target.c
 mi-cmd-var.o: $(srcdir)/mi/mi-cmd-var.c $(defs_h) $(mi_cmds_h) $(ui_out_h) \
        $(mi_out_h) $(varobj_h) $(value_h) $(gdb_string_h)
        $(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-cmd-var.c
index 22283fd..d60402f 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -28,6 +28,9 @@ registers on PowerPC targets.
 * The GDB remote stub, gdbserver, now supports thread debugging on GNU/Linux
 targets even when the libthread_db library is not available.
 
+* The GDB remote stub, gdbserver, now supports the new file transfer
+commands (remote put, remote get, and remote delete).
+
 * hppa*64*-*-hpux11* target broken
   The debugger is unable to start a program and fails with the following
   error: "Error trying to get information about dynamic linker".
@@ -37,6 +40,28 @@ targets even when the libthread_db library is not available.
 building a single GDB executable that supports multiple remote
 target architectures.
 
+* New commands
+
+remote put
+remote get
+remote delete
+  Transfer files to and from a remote target, and delete remote files.
+
+* New MI commands
+
+-target-file-put
+-target-file-get
+-target-file-delete
+  Transfer files to and from a remote target, and delete remote files.
+
+* New remote packets
+
+vFile:open:
+vFile:close:
+vFile:pread:
+vFile:pwrite:
+vFile:unlink:
+  Open, close, read, write, and delete files on the remote system.
 
 *** Changes in GDB 6.7
 
index 89ac3e6..181146e 100644 (file)
@@ -1,3 +1,13 @@
+2007-11-30  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gdb.texinfo (Debugging Programs with Multiple Processes): Correct
+       formatting.
+       (Remote Debugging): Add File Transfer section.
+       (Remote Configuration): Document Host I/O packets.
+       (GDB/MI): Add GDB/MI File Transfer Commands section.
+       (Remote Protocol): Add Host I/O Packets section.
+       (Packets): Add vFile.
+
 2007-11-17  Eli Zaretskii  <eliz@gnu.org>
 
        * gdb.texinfo (Set Breaks, Disabling): Clarify behavior of
index af34740..35cc057 100644 (file)
@@ -2480,7 +2480,7 @@ the child process just like any other process which you attached to.
 On some systems, @value{GDBN} provides support for debugging programs that
 create additional processes using the @code{fork} or @code{vfork} functions.
 Currently, the only platforms with this feature are HP-UX (11.x and later
-only?) and GNU/Linux (kernel version 2.5.60 and later).
+only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
 
 By default, when a program forks, @value{GDBN} will continue to debug
 the parent process and the child process will run unimpeded.
@@ -12670,6 +12670,7 @@ configuration of @value{GDBN}; use @code{help target} to list them.
 
 @menu
 * Connecting::                  Connecting to a remote target
+* File Transfer::               Sending files to a remote system
 * Server::                     Using the gdbserver program
 * Remote Configuration::        Remote configuration
 * Remote Stub::                 Implementing a remote stub
@@ -12817,6 +12818,38 @@ can add new commands that only the external monitor will understand
 and implement.
 @end table
 
+@node File Transfer
+@section Sending files to a remote system
+@cindex remote target, file transfer
+@cindex file transfer
+@cindex sending files to remote systems
+
+Some remote targets offer the ability to transfer files over the same
+connection used to communicate with @value{GDBN}.  This is convenient
+for targets accessible through other means, e.g.@: @sc{gnu}/Linux systems
+running @code{gdbserver} over a network interface.  For other targets,
+e.g.@: embedded devices with only a single serial port, this may be
+the only way to upload or download files.
+
+Not all remote targets support these commands.
+
+@table @code
+@kindex remote put
+@item remote put @var{hostfile} @var{targetfile}
+Copy file @var{hostfile} from the host system (the machine running
+@value{GDBN}) to @var{targetfile} on the target system.
+
+@kindex remote get
+@item remote get @var{targetfile} @var{hostfile}
+Copy file @var{targetfile} from the target system to @var{hostfile}
+on the host system.
+
+@kindex remote delete
+@item remote delete @var{targetfile}
+Delete @var{targetfile} from the target system.
+
+@end table
+
 @node Server
 @section Using the @code{gdbserver} Program
 
@@ -13153,6 +13186,25 @@ are:
 @tab @code{QPassSignals}
 @tab @code{handle @var{signal}}
 
+@item @code{hostio-close-packet}
+@tab @code{vFile:close}
+@tab @code{remote get}, @code{remote put}
+
+@item @code{hostio-open-packet}
+@tab @code{vFile:open}
+@tab @code{remote get}, @code{remote put}
+
+@item @code{hostio-pread-packet}
+@tab @code{vFile:pread}
+@tab @code{remote get}, @code{remote put}
+
+@item @code{hostio-pwrite-packet}
+@tab @code{vFile:pwrite}
+@tab @code{remote get}, @code{remote put}
+
+@item @code{hostio-unlink-packet}
+@tab @code{vFile:unlink}
+@tab @code{remote delete}
 @end multitable
 
 @node Remote Stub
@@ -17349,6 +17401,7 @@ may repeat one or more times.
 * GDB/MI Signal Handling Commands::
 @end ignore
 * GDB/MI Target Manipulation::
+* GDB/MI File Transfer Commands::
 * GDB/MI Miscellaneous Commands::
 @end menu
 
@@ -21296,6 +21349,88 @@ The corresponding @value{GDBN} command is @samp{target}.
 @end smallexample
 
 @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@node GDB/MI File Transfer Commands
+@section @sc{gdb/mi} File Transfer Commands
+
+
+@subheading The @code{-target-file-put} Command
+@findex -target-file-put
+
+@subsubheading Synopsis
+
+@smallexample
+ -target-file-put @var{hostfile} @var{targetfile}
+@end smallexample
+
+Copy file @var{hostfile} from the host system (the machine running
+@value{GDBN}) to @var{targetfile} on the target system.
+
+@subsubheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{remote put}.
+
+@subsubheading Example
+
+@smallexample
+(gdb)
+-target-file-put localfile remotefile
+^done
+(gdb)
+@end smallexample
+
+
+@subheading The @code{-target-file-put} Command
+@findex -target-file-get
+
+@subsubheading Synopsis
+
+@smallexample
+ -target-file-get @var{targetfile} @var{hostfile}
+@end smallexample
+
+Copy file @var{targetfile} from the target system to @var{hostfile}
+on the host system.
+
+@subsubheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{remote get}.
+
+@subsubheading Example
+
+@smallexample
+(gdb)
+-target-file-get remotefile localfile
+^done
+(gdb)
+@end smallexample
+
+
+@subheading The @code{-target-file-delete} Command
+@findex -target-file-delete
+
+@subsubheading Synopsis
+
+@smallexample
+ -target-file-delete @var{targetfile}
+@end smallexample
+
+Delete @var{targetfile} from the target system.
+
+@subsubheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{remote delete}.
+
+@subsubheading Example
+
+@smallexample
+(gdb)
+-target-file-delete remotefile
+^done
+(gdb)
+@end smallexample
+
+
+@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 @node GDB/MI Miscellaneous Commands
 @section Miscellaneous @sc{gdb/mi} Commands
 
@@ -22920,6 +23055,7 @@ Show the current setting of the target wait timeout.
 * General Query Packets::
 * Register Packet Format::
 * Tracepoint Packets::
+* Host I/O Packets::
 * Interrupts::
 * Examples::
 * File-I/O Remote Protocol Extension::
@@ -23423,6 +23559,11 @@ command in the @samp{vCont} packet.
 The @samp{vCont} packet is not supported.
 @end table
 
+@item vFile:@var{operation}:@var{parameter}@dots{}
+@cindex @samp{vFile} packet
+Perform a file operation on the target system.  For details,
+see @ref{Host I/O Packets}.
+
 @item vFlashErase:@var{addr},@var{length}
 @cindex @samp{vFlashErase} packet
 Direct the stub to erase @var{length} bytes of flash starting at
@@ -24572,6 +24713,104 @@ There is a trace experiment running.
 @end table
 
 
+@node Host I/O Packets
+@section Host I/O Packets
+@cindex Host I/O, remote protocol
+@cindex file transfer, remote protocol
+
+The @dfn{Host I/O} packets allow @value{GDBN} to perform I/O
+operations on the far side of a remote link.  For example, Host I/O is
+used to upload and download files to a remote target with its own
+filesystem.  Host I/O uses the same constant values and data structure
+layout as the target-initiated File-I/O protocol.  However, the
+Host I/O packets are structured differently.  The target-initiated
+protocol relies on target memory to store parameters and buffers.
+Host I/O requests are initiated by @value{GDBN}, and the
+target's memory is not involved.  @xref{File-I/O Remote Protocol
+Extension}, for more details on the target-initiated protocol.
+
+The Host I/O request packets all encode a single operation along with
+its arguments.  They have this format:
+
+@table @samp
+
+@item vFile:@var{operation}: @var{parameter}@dots{}
+@var{operation} is the name of the particular request; the target
+should compare the entire packet name up to the second colon when checking
+for a supported operation.  The format of @var{parameter} depends on
+the operation.  Numbers are always passed in hexadecimal.  Negative
+numbers have an explicit minus sign (i.e.@: two's complement is not
+used).  Strings (e.g.@: filenames) are encoded as a series of
+hexadecimal bytes.  The last argument to a system call may be a
+buffer of escaped binary data (@pxref{Binary Data}).
+
+@end table
+
+The valid responses to Host I/O packets are:
+
+@table @samp
+
+@item F @var{result} [, @var{errno}] [; @var{attachment}]
+@var{result} is the integer value returned by this operation, usually
+non-negative for success and -1 for errors.  If an error has occured,
+@var{errno} will be included in the result.  @var{errno} will have a
+value defined by the File-I/O protocol (@pxref{Errno Values}).  For
+operations which return data, @var{attachment} supplies the data as a
+binary buffer.  Binary buffers in response packets are escaped in the
+normal way (@pxref{Binary Data}).  See the individual packet
+documentation for the interpretation of @var{result} and
+@var{attachment}.
+
+@item
+An empty response indicates that this operation is not recognized.
+
+@end table
+
+These are the supported Host I/O operations:
+
+@table @samp
+@item vFile:open: @var{pathname}, @var{flags}, @var{mode}
+Open a file at @var{pathname} and return a file descriptor for it, or
+return -1 if an error occurs.  @var{pathname} is a string,
+@var{flags} is an integer indicating a mask of open flags
+(@pxref{Open Flags}), and @var{mode} is an integer indicating a mask
+of mode bits to use if the file is created (@pxref{mode_t Values}).
+@xref{open} for details of the open flags and mode values.
+
+@item vFile:close: @var{fd}
+Close the open file corresponding to @var{fd} and return 0, or
+-1 if an error occurs.
+
+@item vFile:pread: @var{fd}, @var{count}, @var{offset}
+Read data from the open file corresponding to @var{fd}.  Up to
+@var{count} bytes will be read from the file, starting at @var{offset}
+relative to the start of the file.  The target may read fewer bytes;
+common reasons include packet size limits and an end-of-file
+condition.  The number of bytes read is returned.  Zero should only be
+returned for a successful read at the end of the file, or if
+@var{count} was zero.
+
+The data read should be returned as a binary attachment on success.
+If zero bytes were read, the response should include an empty binary
+attachment (i.e.@: a trailing semicolon).  The return value is the
+number of target bytes read; the binary attachment may be longer if
+some characters were escaped.
+
+@item vFile:pwrite: @var{fd}, @var{offset}, @var{data}
+Write @var{data} (a binary buffer) to the open file corresponding
+to @var{fd}.  Start the write at @var{offset} from the start of the
+file.  Unlike many @code{write} system calls, there is no
+separate @var{count} argument; the length of @var{data} in the
+packet is used.  @samp{vFile:write} returns the number of bytes written,
+which may be shorter than the length of @var{data}, or -1 if an
+error occurred.
+
+@item vFile:unlink: @var{pathname}
+Delete the file at @var{pathname} on the target.  Return 0,
+or -1 if an error occurs.  @var{pathname} is a string.
+
+@end table
+
 @node Interrupts
 @section Interrupts
 @cindex interrupts (remote protocol)
index 245163b..aa1661f 100644 (file)
@@ -1,3 +1,13 @@
+2007-11-30  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * Makefile.in (OBS): Add hostio.o.
+       (hostio.o): New rule.
+       * server.h (handle_vFile): Declare.
+       * hostio.c: New file.
+       * server.c (handle_v_requests): Take packet_len and new_packet_len
+       for binary packets.  Call handle_vFile.
+       (main): Update call to handle_v_requests.
+
 2007-11-05  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * linux-low.c: Include <sched.h>.
index 9ac7c44..9658ce3 100644 (file)
@@ -139,7 +139,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS}
 
 OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \
        utils.o version.o \
-       mem-break.o \
+       mem-break.o hostio.o \
        $(XML_BUILTIN) \
        $(DEPFILES)
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -277,6 +277,7 @@ regcache_h = $(srcdir)/regcache.h
 server_h = $(srcdir)/server.h $(regcache_h) config.h $(srcdir)/target.h \
                $(srcdir)/mem-break.h
 
+hostio.o: hostio.c $(server_h)
 inferiors.o: inferiors.c $(server_h)
 mem-break.o: mem-break.c $(server_h)
 proc-service.o: proc-service.c $(server_h) $(gdb_proc_service_h)
diff --git a/gdb/gdbserver/hostio.c b/gdb/gdbserver/hostio.c
new file mode 100644 (file)
index 0000000..5160ba9
--- /dev/null
@@ -0,0 +1,517 @@
+/* Host file transfer support for gdbserver.
+   Copyright (C) 2006
+   Free Software Foundation, Inc.
+
+   Contributed by CodeSourcery.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "server.h"
+#include "gdb/fileio.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+extern int remote_debug;
+
+struct fd_list
+{
+  int fd;
+  struct fd_list *next;
+};
+
+static struct fd_list *open_fds;
+
+static int
+safe_fromhex (char a, int *nibble)
+{
+  if (a >= '0' && a <= '9')
+    *nibble = a - '0';
+  else if (a >= 'a' && a <= 'f')
+    *nibble = a - 'a' + 10;
+  else if (a >= 'A' && a <= 'F')
+    *nibble = a - 'A' + 10;
+  else
+    return -1;
+
+  return 0;
+}
+
+static int
+require_filename (char **pp, char *filename)
+{
+  int count;
+  char *p;
+
+  p = *pp;
+  count = 0;
+
+  while (*p && *p != ',')
+    {
+      int nib1, nib2;
+
+      /* Don't allow overflow.  */
+      if (count >= PATH_MAX - 1)
+       return -1;
+
+      if (safe_fromhex (p[0], &nib1)
+         || safe_fromhex (p[1], &nib2))
+       return -1;
+
+      filename[count++] = nib1 * 16 + nib2;
+      p += 2;
+    }
+
+  filename[count] = '\0';
+  *pp = p;
+  return 0;
+}
+
+static int
+require_int (char **pp, int *value)
+{
+  char *p;
+  int count;
+
+  p = *pp;
+  *value = 0;
+  count = 0;
+
+  while (*p && *p != ',')
+    {
+      int nib;
+
+      /* Don't allow overflow.  */
+      if (count >= 7)
+       return -1;
+
+      if (safe_fromhex (p[0], &nib))
+       return -1;
+      *value = *value * 16 + nib;
+      p++;
+      count++;
+    }
+
+  *pp = p;
+  return 0;
+}
+
+static int
+require_data (char *p, int p_len, char **data, int *data_len)
+{
+  int input_index, output_index, escaped;
+
+  *data = malloc (p_len);
+
+  output_index = 0;
+  escaped = 0;
+  for (input_index = 0; input_index < p_len; input_index++)
+    {
+      char b = p[input_index];
+
+      if (escaped)
+       {
+         (*data)[output_index++] = b ^ 0x20;
+         escaped = 0;
+       }
+      else if (b == '}')
+       escaped = 1;
+      else
+       (*data)[output_index++] = b;
+    }
+
+  if (escaped)
+    return -1;
+
+  *data_len = output_index;
+  return 0;
+}
+
+static int
+require_comma (char **pp)
+{
+  if (**pp == ',')
+    {
+      (*pp)++;
+      return 0;
+    }
+  else
+    return -1;
+}
+
+static int
+require_end (char *p)
+{
+  if (*p == '\0')
+    return 0;
+  else
+    return -1;
+}
+
+static int
+require_valid_fd (int fd)
+{
+  struct fd_list *fd_ptr;
+
+  for (fd_ptr = open_fds; fd_ptr != NULL; fd_ptr = fd_ptr->next)
+    if (fd_ptr->fd == fd)
+      return 0;
+
+  return -1;
+}
+
+static int
+errno_to_fileio_errno (int error)
+{
+  switch (error)
+    {
+    case EPERM:
+      return FILEIO_EPERM;
+    case ENOENT:
+      return FILEIO_ENOENT;
+    case EINTR:
+      return FILEIO_EINTR;
+    case EIO:
+      return FILEIO_EIO;
+    case EBADF:
+      return FILEIO_EBADF;
+    case EACCES:
+      return FILEIO_EACCES;
+    case EFAULT:
+      return FILEIO_EFAULT;
+    case EBUSY:
+      return FILEIO_EBUSY;
+    case EEXIST:
+      return FILEIO_EEXIST;
+    case ENODEV:
+      return FILEIO_ENODEV;
+    case ENOTDIR:
+      return FILEIO_ENOTDIR;
+    case EISDIR:
+      return FILEIO_EISDIR;
+    case EINVAL:
+      return FILEIO_EINVAL;
+    case ENFILE:
+      return FILEIO_ENFILE;
+    case EMFILE:
+      return FILEIO_EMFILE;
+    case EFBIG:
+      return FILEIO_EFBIG;
+    case ENOSPC:
+      return FILEIO_ENOSPC;
+    case ESPIPE:
+      return FILEIO_ESPIPE;
+    case EROFS:
+      return FILEIO_EROFS;
+    case ENOSYS:
+      return FILEIO_ENOSYS;
+    case ENAMETOOLONG:
+      return FILEIO_ENAMETOOLONG;
+    }
+  return FILEIO_EUNKNOWN;
+}
+
+static void
+hostio_error (char *own_buf, int error)
+{
+  int fileio_error = errno_to_fileio_errno (error);
+
+  sprintf (own_buf, "F-1,%x", fileio_error);
+}
+
+static void
+hostio_packet_error (char *own_buf)
+{
+  hostio_error (own_buf, EINVAL);
+}
+
+static void
+hostio_reply (char *own_buf, int result)
+{
+  sprintf (own_buf, "F%x", result);
+}
+
+static int
+hostio_reply_with_data (char *own_buf, char *buffer, int len,
+                       int *new_packet_len)
+{
+  int input_index, output_index, out_maxlen;
+
+  sprintf (own_buf, "F%x;", len);
+  output_index = strlen (own_buf);
+
+  out_maxlen = PBUFSIZ;
+
+  for (input_index = 0; input_index < len; input_index++)
+    {
+      char b = buffer[input_index];
+
+      if (b == '$' || b == '#' || b == '}' || b == '*')
+       {
+         /* These must be escaped.  */
+         if (output_index + 2 > out_maxlen)
+           break;
+         own_buf[output_index++] = '}';
+         own_buf[output_index++] = b ^ 0x20;
+       }
+      else
+       {
+         if (output_index + 1 > out_maxlen)
+           break;
+         own_buf[output_index++] = b;
+       }
+    }
+
+  *new_packet_len = output_index;
+  return input_index;
+}
+
+static int
+fileio_open_flags_to_host (int fileio_open_flags, int *open_flags_p)
+{
+  int open_flags = 0;
+
+  if (fileio_open_flags & ~FILEIO_O_SUPPORTED)
+    return -1;
+
+  if (fileio_open_flags & FILEIO_O_CREAT)
+    open_flags |= O_CREAT;
+  if (fileio_open_flags & FILEIO_O_EXCL)
+    open_flags |= O_EXCL;
+  if (fileio_open_flags & FILEIO_O_TRUNC)
+    open_flags |= O_TRUNC;
+  if (fileio_open_flags & FILEIO_O_APPEND)
+    open_flags |= O_APPEND;
+  if (fileio_open_flags & FILEIO_O_RDONLY)
+    open_flags |= O_RDONLY;
+  if (fileio_open_flags & FILEIO_O_WRONLY)
+    open_flags |= O_WRONLY;
+  if (fileio_open_flags & FILEIO_O_RDWR)
+    open_flags |= O_RDWR;
+/* On systems supporting binary and text mode, always open files in
+   binary mode. */
+#ifdef O_BINARY
+  open_flags |= O_BINARY;
+#endif
+
+  *open_flags_p = open_flags;
+  return 0;
+}
+
+static void
+handle_open (char *own_buf)
+{
+  char filename[PATH_MAX];
+  char *p;
+  int fileio_flags, mode, flags, fd;
+  struct fd_list *new_fd;
+
+  p = own_buf + strlen ("vFile:open:");
+
+  if (require_filename (&p, filename)
+      || require_comma (&p)
+      || require_int (&p, &fileio_flags)
+      || require_comma (&p)
+      || require_int (&p, &mode)
+      || require_end (p)
+      || fileio_open_flags_to_host (fileio_flags, &flags))
+    {
+      hostio_packet_error (own_buf);
+      return;
+    }
+
+  /* We do not need to convert MODE, since the fileio protocol
+     uses the standard values.  */
+  fd = open (filename, flags, mode);
+
+  if (fd == -1)
+    {
+      hostio_error (own_buf, errno);
+      return;
+    }
+
+  /* Record the new file descriptor.  */
+  new_fd = malloc (sizeof (struct fd_list));
+  new_fd->fd = fd;
+  new_fd->next = open_fds;
+  open_fds = new_fd;
+
+  hostio_reply (own_buf, fd);
+}
+
+static void
+handle_pread (char *own_buf, int *new_packet_len)
+{
+  int fd, ret, len, offset, bytes_sent;
+  char *p, *data;
+
+  p = own_buf + strlen ("vFile:pread:");
+
+  if (require_int (&p, &fd)
+      || require_comma (&p)
+      || require_valid_fd (fd)
+      || require_int (&p, &len)
+      || require_comma (&p)
+      || require_int (&p, &offset)
+      || require_end (p))
+    {
+      hostio_packet_error (own_buf);
+      return;
+    }
+
+  data = malloc (len);
+  ret = pread (fd, data, len, offset);
+
+  if (ret == -1)
+    {
+      hostio_error (own_buf, errno);
+      free (data);
+      return;
+    }
+
+  bytes_sent = hostio_reply_with_data (own_buf, data, ret, new_packet_len);
+
+  /* If we were using read, and the data did not all fit in the reply,
+     we would have to back up using lseek here.  With pread it does
+     not matter.  But we still have a problem; the return value in the
+     packet might be wrong, so we must fix it.  This time it will
+     definitely fit.  */
+  if (bytes_sent < ret)
+    bytes_sent = hostio_reply_with_data (own_buf, data, bytes_sent,
+                                        new_packet_len);
+
+  free (data);
+}
+
+static void
+handle_pwrite (char *own_buf, int packet_len)
+{
+  int fd, ret, len, offset;
+  char *p, *data;
+
+  p = own_buf + strlen ("vFile:pwrite:");
+
+  if (require_int (&p, &fd)
+      || require_comma (&p)
+      || require_valid_fd (fd)
+      || require_int (&p, &offset)
+      || require_comma (&p)
+      || require_data (p, packet_len - (p - own_buf), &data, &len))
+    {
+      hostio_packet_error (own_buf);
+      return;
+    }
+
+  ret = pwrite (fd, data, len, offset);
+
+  if (ret == -1)
+    {
+      hostio_error (own_buf, errno);
+      free (data);
+      return;
+    }
+
+  hostio_reply (own_buf, ret);
+  free (data);
+}
+
+static void
+handle_close (char *own_buf)
+{
+  int fd, ret;
+  char *p;
+  struct fd_list **open_fd_p, *old_fd;
+
+  p = own_buf + strlen ("vFile:close:");
+
+  if (require_int (&p, &fd)
+      || require_valid_fd (fd)
+      || require_end (p))
+    {
+      hostio_packet_error (own_buf);
+      return;
+    }
+
+  ret = close (fd);
+
+  if (ret == -1)
+    {
+      hostio_error (own_buf, errno);
+      return;
+    }
+
+  open_fd_p = &open_fds;
+  while (*open_fd_p && (*open_fd_p)->fd != fd)
+    open_fd_p = &(*open_fd_p)->next;
+
+  old_fd = *open_fd_p;
+  *open_fd_p = (*open_fd_p)->next;
+  free (old_fd);
+
+  hostio_reply (own_buf, ret);
+}
+
+static void
+handle_unlink (char *own_buf)
+{
+  char filename[PATH_MAX];
+  char *p;
+  int ret;
+
+  p = own_buf + strlen ("vFile:unlink:");
+
+  if (require_filename (&p, filename)
+      || require_end (p))
+    {
+      hostio_packet_error (own_buf);
+      return;
+    }
+
+  ret = unlink (filename);
+
+  if (ret == -1)
+    {
+      hostio_error (own_buf, errno);
+      return;
+    }
+
+  hostio_reply (own_buf, ret);
+}
+
+/* Handle all the 'F' file transfer packets.  */
+
+int
+handle_vFile (char *own_buf, int packet_len, int *new_packet_len)
+{
+  if (strncmp (own_buf, "vFile:open:", 11) == 0)
+    handle_open (own_buf);
+  else if (strncmp (own_buf, "vFile:pread:", 11) == 0)
+    handle_pread (own_buf, new_packet_len);
+  else if (strncmp (own_buf, "vFile:pwrite:", 12) == 0)
+    handle_pwrite (own_buf, packet_len);
+  else if (strncmp (own_buf, "vFile:close:", 12) == 0)
+    handle_close (own_buf);
+  else if (strncmp (own_buf, "vFile:unlink:", 13) == 0)
+    handle_unlink (own_buf);
+  else
+    return 0;
+
+  return 1;
+}
index 991ed15..0261e6b 100644 (file)
@@ -772,7 +772,8 @@ err:
 
 /* Handle all of the extended 'v' packets.  */
 void
-handle_v_requests (char *own_buf, char *status, int *signal)
+handle_v_requests (char *own_buf, char *status, int *signal,
+                  int packet_len, int *new_packet_len)
 {
   if (strncmp (own_buf, "vCont;", 6) == 0)
     {
@@ -786,6 +787,10 @@ handle_v_requests (char *own_buf, char *status, int *signal)
       return;
     }
 
+  if (strncmp (own_buf, "vFile:", 6) == 0
+      && handle_vFile (own_buf, packet_len, new_packet_len))
+    return;
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -1218,8 +1223,10 @@ main (int argc, char *argv[])
                }
            case 'v':
              /* Extended (long) request.  */
-             handle_v_requests (own_buf, &status, &signal);
+             handle_v_requests (own_buf, &status, &signal,
+                                packet_len, &new_packet_len);
              break;
+
            default:
              /* It is a request we don't understand.  Respond with an
                 empty packet so that gdb knows that we don't support this
index d350f44..54ba614 100644 (file)
@@ -156,6 +156,9 @@ extern int pass_signals[];
 
 extern jmp_buf toplevel;
 
+/* Functions from hostio.c.  */
+extern int handle_vFile (char *, int, int *);
+
 /* From remote-utils.c */
 
 extern int remote_debug;
diff --git a/gdb/mi/mi-cmd-target.c b/gdb/mi/mi-cmd-target.c
new file mode 100644 (file)
index 0000000..4d33bba
--- /dev/null
@@ -0,0 +1,100 @@
+/* MI Command Set - target commands.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "mi-cmds.h"
+#include "mi-getopt.h"
+#include "remote.h"
+
+/* Get a file from the target.  */
+
+enum mi_cmd_result
+mi_cmd_target_file_get (char *command, char **argv, int argc)
+{
+  int optind = 0;
+  char *optarg;
+  const char *remote_file, *local_file;
+  static struct mi_opt opts[] =
+  {
+    { 0, 0, 0 }
+  };
+  static const char *prefix = "mi_cmd_target_file_get";
+
+  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
+      || optind != argc - 2)
+    error (_("mi_cmd_target_file_get: Usage: REMOTE_FILE LOCAL_FILE"));
+
+  remote_file = argv[optind];
+  local_file = argv[optind + 1];
+
+  remote_file_get (remote_file, local_file, 0);
+
+  return MI_CMD_DONE;
+}
+
+/* Send a file to the target.  */
+
+enum mi_cmd_result
+mi_cmd_target_file_put (char *command, char **argv, int argc)
+{
+  int optind = 0;
+  char *optarg;
+  const char *remote_file, *local_file;
+  static struct mi_opt opts[] =
+  {
+    { 0, 0, 0 }
+  };
+  static const char *prefix = "mi_cmd_target_file_put";
+
+  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
+      || optind != argc - 2)
+    error (_("mi_cmd_target_file_put: Usage: LOCAL_FILE REMOTE_FILE"));
+
+  local_file = argv[optind];
+  remote_file = argv[optind + 1];
+
+  remote_file_put (local_file, remote_file, 0);
+
+  return MI_CMD_DONE;
+}
+
+/* Delete a file on the target.  */
+
+enum mi_cmd_result
+mi_cmd_target_file_delete (char *command, char **argv, int argc)
+{
+  int optind = 0;
+  char *optarg;
+  const char *remote_file, *local_file;
+  static struct mi_opt opts[] =
+  {
+    { 0, 0, 0 }
+  };
+  static const char *prefix = "mi_cmd_target_file_delete";
+
+  if (mi_getopt (prefix, argc, argv, opts, &optind, &optarg) != -1
+      || optind != argc - 1)
+    error (_("mi_cmd_target_file_delete: Usage: REMOTE_FILE"));
+
+  remote_file = argv[optind];
+
+  remote_file_delete (remote_file, 0);
+
+  return MI_CMD_DONE;
+}
+
index 5bd8725..4082d89 100644 (file)
@@ -123,6 +123,9 @@ struct mi_cmd mi_cmds[] =
   { "target-disconnect", { "disconnect", 0 }, 0 },
   { "target-download", { NULL, 0 }, mi_cmd_target_download},
   { "target-exec-status", { NULL, 0 }, NULL, NULL },
+  { "target-file-delete", { NULL, 0 }, NULL, mi_cmd_target_file_delete },
+  { "target-file-get", { NULL, 0 }, NULL, mi_cmd_target_file_get },
+  { "target-file-put", { NULL, 0 }, NULL, mi_cmd_target_file_put },
   { "target-list-available-targets", { NULL, 0 }, NULL, NULL },
   { "target-list-current-targets", { NULL, 0 }, NULL, NULL },
   { "target-list-parameters", { NULL, 0 }, NULL, NULL },
index 9888c85..9ea08e8 100644 (file)
@@ -100,6 +100,9 @@ extern mi_cmd_argv_ftype mi_cmd_stack_list_locals;
 extern mi_cmd_argv_ftype mi_cmd_stack_select_frame;
 extern mi_cmd_argv_ftype mi_cmd_symbol_list_lines;
 extern mi_cmd_args_ftype mi_cmd_target_download;
+extern mi_cmd_argv_ftype mi_cmd_target_file_get;
+extern mi_cmd_argv_ftype mi_cmd_target_file_put;
+extern mi_cmd_argv_ftype mi_cmd_target_file_delete;
 extern mi_cmd_args_ftype mi_cmd_target_select;
 extern mi_cmd_argv_ftype mi_cmd_thread_list_ids;
 extern mi_cmd_argv_ftype mi_cmd_thread_select;
index 2d8a88b..ec7044e 100644 (file)
@@ -58,6 +58,7 @@
 #include "gdbcore.h" /* for exec_bfd */
 
 #include "remote-fileio.h"
+#include "gdb/fileio.h"
 
 #include "memory-map.h"
 
@@ -207,6 +208,10 @@ static void show_remote_protocol_packet_cmd (struct ui_file *file,
 
 void _initialize_remote (void);
 
+/* For "remote".  */
+
+static struct cmd_list_element *remote_cmdlist;
+
 /* For "set remote" and "show remote".  */
 
 static struct cmd_list_element *remote_set_cmdlist;
@@ -901,6 +906,11 @@ enum {
   PACKET_Z2,
   PACKET_Z3,
   PACKET_Z4,
+  PACKET_vFile_open,
+  PACKET_vFile_pread,
+  PACKET_vFile_pwrite,
+  PACKET_vFile_close,
+  PACKET_vFile_unlink,
   PACKET_qXfer_auxv,
   PACKET_qXfer_features,
   PACKET_qXfer_libraries,
@@ -6276,6 +6286,631 @@ remote_read_description (struct target_ops *target)
   return NULL;
 }
 
+/* Remote file transfer support.  This is host-initiated I/O, not
+   target-initiated; for target-initiated, see remote-fileio.c.  */
+
+/* If *LEFT is at least the length of STRING, copy STRING to
+   *BUFFER, update *BUFFER to point to the new end of the buffer, and
+   decrease *LEFT.  Otherwise raise an error.  */
+
+static void
+remote_buffer_add_string (char **buffer, int *left, char *string)
+{
+  int len = strlen (string);
+
+  if (len > *left)
+    error (_("Packet too long for target."));
+
+  memcpy (*buffer, string, len);
+  *buffer += len;
+  *left -= len;
+
+  /* NUL-terminate the buffer as a convenience, if there is
+     room.  */
+  if (*left)
+    **buffer = '\0';
+}
+
+/* If *LEFT is large enough, hex encode LEN bytes from BYTES into
+   *BUFFER, update *BUFFER to point to the new end of the buffer, and
+   decrease *LEFT.  Otherwise raise an error.  */
+
+static void
+remote_buffer_add_bytes (char **buffer, int *left, const gdb_byte *bytes,
+                        int len)
+{
+  if (2 * len > *left)
+    error (_("Packet too long for target."));
+
+  bin2hex (bytes, *buffer, len);
+  *buffer += 2 * len;
+  *left -= 2 * len;
+
+  /* NUL-terminate the buffer as a convenience, if there is
+     room.  */
+  if (*left)
+    **buffer = '\0';
+}
+
+/* If *LEFT is large enough, convert VALUE to hex and add it to
+   *BUFFER, update *BUFFER to point to the new end of the buffer, and
+   decrease *LEFT.  Otherwise raise an error.  */
+
+static void
+remote_buffer_add_int (char **buffer, int *left, ULONGEST value)
+{
+  int len = hexnumlen (value);
+
+  if (len > *left)
+    error (_("Packet too long for target."));
+
+  hexnumstr (*buffer, value);
+  *buffer += len;
+  *left -= len;
+
+  /* NUL-terminate the buffer as a convenience, if there is
+     room.  */
+  if (*left)
+    **buffer = '\0';
+}
+
+/* Parse an I/O result packet from BUFFER.  Set RETCODE to the return
+   value, *REMOTE_ERRNO to the remote error number or zero if none
+   was included, and *ATTACHMENT to point to the start of the annex
+   if any.  The length of the packet isn't needed here; there may
+   be NUL bytes in BUFFER, but they will be after *ATTACHMENT.
+
+   Return 0 if the packet could be parsed, -1 if it could not.  If
+   -1 is returned, the other variables may not be initialized.  */
+
+static int
+remote_hostio_parse_result (char *buffer, int *retcode,
+                           int *remote_errno, char **attachment)
+{
+  char *p, *p2;
+
+  *remote_errno = 0;
+  *attachment = NULL;
+
+  if (buffer[0] != 'F')
+    return -1;
+
+  errno = 0;
+  *retcode = strtol (&buffer[1], &p, 16);
+  if (errno != 0 || p == &buffer[1])
+    return -1;
+
+  /* Check for ",errno".  */
+  if (*p == ',')
+    {
+      errno = 0;
+      *remote_errno = strtol (p + 1, &p2, 16);
+      if (errno != 0 || p + 1 == p2)
+       return -1;
+      p = p2;
+    }
+
+  /* Check for ";attachment".  If there is no attachment, the
+     packet should end here.  */
+  if (*p == ';')
+    {
+      *attachment = p + 1;
+      return 0;
+    }
+  else if (*p == '\0')
+    return 0;
+  else
+    return -1;
+}
+
+/* Send a prepared I/O packet to the target and read its response.
+   The prepared packet is in the global RS->BUF before this function
+   is called, and the answer is there when we return.
+
+   COMMAND_BYTES is the length of the request to send, which may include
+   binary data.  WHICH_PACKET is the packet configuration to check
+   before attempting a packet.  If an error occurs, *REMOTE_ERRNO
+   is set to the error number and -1 is returned.  Otherwise the value
+   returned by the function is returned.
+
+   ATTACHMENT and ATTACHMENT_LEN should be non-NULL if and only if an
+   attachment is expected; an error will be reported if there's a
+   mismatch.  If one is found, *ATTACHMENT will be set to point into
+   the packet buffer and *ATTACHMENT_LEN will be set to the
+   attachment's length.  */
+
+static int
+remote_hostio_send_command (int command_bytes, int which_packet,
+                           int *remote_errno, char **attachment,
+                           int *attachment_len)
+{
+  struct remote_state *rs = get_remote_state ();
+  int ret, bytes_read;
+  char *attachment_tmp;
+
+  if (remote_protocol_packets[which_packet].support == PACKET_DISABLE)
+    {
+      *remote_errno = FILEIO_ENOSYS;
+      return -1;
+    }
+
+  putpkt_binary (rs->buf, command_bytes);
+  bytes_read = getpkt_sane (&rs->buf, &rs->buf_size, 0);
+
+  /* If it timed out, something is wrong.  Don't try to parse the
+     buffer.  */
+  if (bytes_read < 0)
+    {
+      *remote_errno = FILEIO_EINVAL;
+      return -1;
+    }
+
+  switch (packet_ok (rs->buf, &remote_protocol_packets[which_packet]))
+    {
+    case PACKET_ERROR:
+      *remote_errno = FILEIO_EINVAL;
+      return -1;
+    case PACKET_UNKNOWN:
+      *remote_errno = FILEIO_ENOSYS;
+      return -1;
+    case PACKET_OK:
+      break;
+    }
+
+  if (remote_hostio_parse_result (rs->buf, &ret, remote_errno,
+                                 &attachment_tmp))
+    {
+      *remote_errno = FILEIO_EINVAL;
+      return -1;
+    }
+
+  /* Make sure we saw an attachment if and only if we expected one.  */
+  if ((attachment_tmp == NULL && attachment != NULL)
+      || (attachment_tmp != NULL && attachment == NULL))
+    {
+      *remote_errno = FILEIO_EINVAL;
+      return -1;
+    }
+
+  /* If an attachment was found, it must point into the packet buffer;
+     work out how many bytes there were.  */
+  if (attachment_tmp != NULL)
+    {
+      *attachment = attachment_tmp;
+      *attachment_len = bytes_read - (*attachment - rs->buf);
+    }
+
+  return ret;
+}
+
+/* Open FILENAME on the remote target, using FLAGS and MODE.  Return a
+   remote file descriptor, or -1 if an error occurs (and set
+   *REMOTE_ERRNO).  */
+
+static int
+remote_hostio_open (const char *filename, int flags, int mode,
+                   int *remote_errno)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf;
+  int left = get_remote_packet_size () - 1;
+
+  remote_buffer_add_string (&p, &left, "vFile:open:");
+
+  remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename,
+                          strlen (filename));
+  remote_buffer_add_string (&p, &left, ",");
+
+  remote_buffer_add_int (&p, &left, flags);
+  remote_buffer_add_string (&p, &left, ",");
+
+  remote_buffer_add_int (&p, &left, mode);
+
+  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_open,
+                                    remote_errno, NULL, NULL);
+}
+
+/* Write up to LEN bytes from WRITE_BUF to FD on the remote target.
+   Return the number of bytes written, or -1 if an error occurs (and
+   set *REMOTE_ERRNO).  */
+
+static int
+remote_hostio_pwrite (int fd, const gdb_byte *write_buf, int len,
+                     ULONGEST offset, int *remote_errno)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf;
+  int left = get_remote_packet_size ();
+  int out_len;
+
+  remote_buffer_add_string (&p, &left, "vFile:pwrite:");
+
+  remote_buffer_add_int (&p, &left, fd);
+  remote_buffer_add_string (&p, &left, ",");
+
+  remote_buffer_add_int (&p, &left, offset);
+  remote_buffer_add_string (&p, &left, ",");
+
+  p += remote_escape_output (write_buf, len, p, &out_len,
+                            get_remote_packet_size () - (p - rs->buf));
+
+  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_pwrite,
+                                    remote_errno, NULL, NULL);
+}
+
+/* Read up to LEN bytes FD on the remote target into READ_BUF
+   Return the number of bytes read, or -1 if an error occurs (and
+   set *REMOTE_ERRNO).  */
+
+static int
+remote_hostio_pread (int fd, gdb_byte *read_buf, int len,
+                    ULONGEST offset, int *remote_errno)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf;
+  char *attachment;
+  int left = get_remote_packet_size ();
+  int ret, attachment_len;
+  int read_len;
+
+  remote_buffer_add_string (&p, &left, "vFile:pread:");
+
+  remote_buffer_add_int (&p, &left, fd);
+  remote_buffer_add_string (&p, &left, ",");
+
+  remote_buffer_add_int (&p, &left, len);
+  remote_buffer_add_string (&p, &left, ",");
+
+  remote_buffer_add_int (&p, &left, offset);
+
+  ret = remote_hostio_send_command (p - rs->buf, PACKET_vFile_pread,
+                                   remote_errno, &attachment,
+                                   &attachment_len);
+
+  if (ret < 0)
+    return ret;
+
+  read_len = remote_unescape_input (attachment, attachment_len,
+                                   read_buf, len);
+  if (read_len != ret)
+    error (_("Read returned %d, but %d bytes."), ret, (int) read_len);
+
+  return ret;
+}
+
+/* Close FD on the remote target.  Return 0, or -1 if an error occurs
+   (and set *REMOTE_ERRNO).  */
+
+static int
+remote_hostio_close (int fd, int *remote_errno)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf;
+  int left = get_remote_packet_size () - 1;
+
+  remote_buffer_add_string (&p, &left, "vFile:close:");
+
+  remote_buffer_add_int (&p, &left, fd);
+
+  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_close,
+                                    remote_errno, NULL, NULL);
+}
+
+/* Unlink FILENAME on the remote target.  Return 0, or -1 if an error
+   occurs (and set *REMOTE_ERRNO).  */
+
+static int
+remote_hostio_unlink (const char *filename, int *remote_errno)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p = rs->buf;
+  int left = get_remote_packet_size () - 1;
+
+  remote_buffer_add_string (&p, &left, "vFile:unlink:");
+
+  remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename,
+                          strlen (filename));
+
+  return remote_hostio_send_command (p - rs->buf, PACKET_vFile_unlink,
+                                    remote_errno, NULL, NULL);
+}
+
+static int
+remote_fileio_errno_to_host (int errnum)
+{
+  switch (errnum)
+    {
+      case FILEIO_EPERM:
+        return EPERM;
+      case FILEIO_ENOENT:
+        return ENOENT;
+      case FILEIO_EINTR:
+        return EINTR;
+      case FILEIO_EIO:
+        return EIO;
+      case FILEIO_EBADF:
+        return EBADF;
+      case FILEIO_EACCES:
+        return EACCES;
+      case FILEIO_EFAULT:
+        return EFAULT;
+      case FILEIO_EBUSY:
+        return EBUSY;
+      case FILEIO_EEXIST:
+        return EEXIST;
+      case FILEIO_ENODEV:
+        return ENODEV;
+      case FILEIO_ENOTDIR:
+        return ENOTDIR;
+      case FILEIO_EISDIR:
+        return EISDIR;
+      case FILEIO_EINVAL:
+        return EINVAL;
+      case FILEIO_ENFILE:
+        return ENFILE;
+      case FILEIO_EMFILE:
+        return EMFILE;
+      case FILEIO_EFBIG:
+        return EFBIG;
+      case FILEIO_ENOSPC:
+        return ENOSPC;
+      case FILEIO_ESPIPE:
+        return ESPIPE;
+      case FILEIO_EROFS:
+        return EROFS;
+      case FILEIO_ENOSYS:
+        return ENOSYS;
+      case FILEIO_ENAMETOOLONG:
+        return ENAMETOOLONG;
+    }
+  return -1;
+}
+
+static char *
+remote_hostio_error (int errnum)
+{
+  int host_error = remote_fileio_errno_to_host (errnum);
+
+  if (host_error == -1)
+    error (_("Unknown remote I/O error %d"), errnum);
+  else
+    error (_("Remote I/O error: %s"), safe_strerror (host_error));
+}
+
+static void
+fclose_cleanup (void *file)
+{
+  fclose (file);
+}
+
+static void
+remote_hostio_close_cleanup (void *opaque)
+{
+  int fd = *(int *) opaque;
+  int remote_errno;
+
+  remote_hostio_close (fd, &remote_errno);
+}
+
+void
+remote_file_put (const char *local_file, const char *remote_file, int from_tty)
+{
+  struct cleanup *back_to, *close_cleanup;
+  int retcode, fd, remote_errno, bytes, io_size;
+  FILE *file;
+  gdb_byte *buffer;
+  int bytes_in_buffer;
+  int saw_eof;
+  ULONGEST offset;
+
+  if (!remote_desc)
+    error (_("command can only be used with remote target"));
+
+  file = fopen (local_file, "rb");
+  if (file == NULL)
+    perror_with_name (local_file);
+  back_to = make_cleanup (fclose_cleanup, file);
+
+  fd = remote_hostio_open (remote_file, (FILEIO_O_WRONLY | FILEIO_O_CREAT
+                                        | FILEIO_O_TRUNC),
+                          0700, &remote_errno);
+  if (fd == -1)
+    remote_hostio_error (remote_errno);
+
+  /* Send up to this many bytes at once.  They won't all fit in the
+     remote packet limit, so we'll transfer slightly fewer.  */
+  io_size = get_remote_packet_size ();
+  buffer = xmalloc (io_size);
+  make_cleanup (xfree, buffer);
+
+  close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd);
+
+  bytes_in_buffer = 0;
+  saw_eof = 0;
+  offset = 0;
+  while (bytes_in_buffer || !saw_eof)
+    {
+      if (!saw_eof)
+       {
+         bytes = fread (buffer + bytes_in_buffer, 1, io_size - bytes_in_buffer,
+                        file);
+         if (bytes == 0)
+           {
+             if (ferror (file))
+               error (_("Error reading %s."), local_file);
+             else
+               {
+                 /* EOF.  Unless there is something still in the
+                    buffer from the last iteration, we are done.  */
+                 saw_eof = 1;
+                 if (bytes_in_buffer == 0)
+                   break;
+               }
+           }
+       }
+      else
+       bytes = 0;
+
+      bytes += bytes_in_buffer;
+      bytes_in_buffer = 0;
+
+      retcode = remote_hostio_pwrite (fd, buffer, bytes, offset, &remote_errno);
+
+      if (retcode < 0)
+       remote_hostio_error (remote_errno);
+      else if (retcode == 0)
+       error (_("Remote write of %d bytes returned 0!"), bytes);
+      else if (retcode < bytes)
+       {
+         /* Short write.  Save the rest of the read data for the next
+            write.  */
+         bytes_in_buffer = bytes - retcode;
+         memmove (buffer, buffer + retcode, bytes_in_buffer);
+       }
+
+      offset += retcode;
+    }
+
+  discard_cleanups (close_cleanup);
+  if (remote_hostio_close (fd, &remote_errno))
+    remote_hostio_error (remote_errno);
+
+  if (from_tty)
+    printf_filtered (_("Successfully sent file \"%s\".\n"), local_file);
+  do_cleanups (back_to);
+}
+
+void
+remote_file_get (const char *remote_file, const char *local_file, int from_tty)
+{
+  struct cleanup *back_to, *close_cleanup;
+  int retcode, fd, remote_errno, bytes, io_size;
+  FILE *file;
+  gdb_byte *buffer;
+  ULONGEST offset;
+
+  if (!remote_desc)
+    error (_("command can only be used with remote target"));
+
+  fd = remote_hostio_open (remote_file, FILEIO_O_RDONLY, 0, &remote_errno);
+  if (fd == -1)
+    remote_hostio_error (remote_errno);
+
+  file = fopen (local_file, "wb");
+  if (file == NULL)
+    perror_with_name (local_file);
+  back_to = make_cleanup (fclose_cleanup, file);
+
+  /* Send up to this many bytes at once.  They won't all fit in the
+     remote packet limit, so we'll transfer slightly fewer.  */
+  io_size = get_remote_packet_size ();
+  buffer = xmalloc (io_size);
+  make_cleanup (xfree, buffer);
+
+  close_cleanup = make_cleanup (remote_hostio_close_cleanup, &fd);
+
+  offset = 0;
+  while (1)
+    {
+      bytes = remote_hostio_pread (fd, buffer, io_size, offset, &remote_errno);
+      if (bytes == 0)
+       /* Success, but no bytes, means end-of-file.  */
+       break;
+      if (bytes == -1)
+       remote_hostio_error (remote_errno);
+
+      offset += bytes;
+
+      bytes = fwrite (buffer, 1, bytes, file);
+      if (bytes == 0)
+       perror_with_name (local_file);
+    }
+
+  discard_cleanups (close_cleanup);
+  if (remote_hostio_close (fd, &remote_errno))
+    remote_hostio_error (remote_errno);
+
+  if (from_tty)
+    printf_filtered (_("Successfully fetched file \"%s\".\n"), remote_file);
+  do_cleanups (back_to);
+}
+
+void
+remote_file_delete (const char *remote_file, int from_tty)
+{
+  int retcode, remote_errno;
+
+  if (!remote_desc)
+    error (_("command can only be used with remote target"));
+
+  retcode = remote_hostio_unlink (remote_file, &remote_errno);
+  if (retcode == -1)
+    remote_hostio_error (remote_errno);
+
+  if (from_tty)
+    printf_filtered (_("Successfully deleted file \"%s\".\n"), remote_file);
+}
+
+static void
+remote_put_command (char *args, int from_tty)
+{
+  struct cleanup *back_to;
+  char **argv;
+
+  argv = buildargv (args);
+  if (argv == NULL)
+    nomem (0);
+  back_to = make_cleanup_freeargv (argv);
+  if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
+    error (_("Invalid parameters to remote put"));
+
+  remote_file_put (argv[0], argv[1], from_tty);
+
+  do_cleanups (back_to);
+}
+
+static void
+remote_get_command (char *args, int from_tty)
+{
+  struct cleanup *back_to;
+  char **argv;
+
+  argv = buildargv (args);
+  if (argv == NULL)
+    nomem (0);
+  back_to = make_cleanup_freeargv (argv);
+  if (argv[0] == NULL || argv[1] == NULL || argv[2] != NULL)
+    error (_("Invalid parameters to remote get"));
+
+  remote_file_get (argv[0], argv[1], from_tty);
+
+  do_cleanups (back_to);
+}
+
+static void
+remote_delete_command (char *args, int from_tty)
+{
+  struct cleanup *back_to;
+  char **argv;
+
+  argv = buildargv (args);
+  if (argv == NULL)
+    nomem (0);
+  back_to = make_cleanup_freeargv (argv);
+  if (argv[0] == NULL || argv[1] != NULL)
+    error (_("Invalid parameters to remote delete"));
+
+  remote_file_delete (argv[0], from_tty);
+
+  do_cleanups (back_to);
+}
+
+static void
+remote_command (char *args, int from_tty)
+{
+  help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
+}
+
 static void
 init_remote_ops (void)
 {
@@ -6719,6 +7354,21 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qSupported],
                         "qSupported", "supported-packets", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_open],
+                        "vFile:open", "hostio-open", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_pread],
+                        "vFile:pread", "hostio-pread", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_pwrite],
+                        "vFile:pwrite", "hostio-pwrite", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_close],
+                        "vFile:close", "hostio-close", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink],
+                        "vFile:unlink", "hostio-unlink", 0);
+
   /* Keep the old ``set remote Z-packet ...'' working.  Each individual
      Z sub-packet has its own set and show commands, but users may
      have sets to this variable in their .gdbinit files (or in their
@@ -6733,6 +7383,24 @@ packets."),
                                show_remote_protocol_Z_packet_cmd, /* FIXME: i18n: Use of remote protocol `Z' packets is %s.  */
                                &remote_set_cmdlist, &remote_show_cmdlist);
 
+  add_prefix_cmd ("remote", class_files, remote_command, _("\
+Manipulate files on the remote system\n\
+Transfer files to and from the remote target system."),
+                 &remote_cmdlist, "remote ",
+                 0 /* allow-unknown */, &cmdlist);
+
+  add_cmd ("put", class_files, remote_put_command,
+          _("Copy a local file to the remote system."),
+          &remote_cmdlist);
+
+  add_cmd ("get", class_files, remote_get_command,
+          _("Copy a remote file to the local system."),
+          &remote_cmdlist);
+
+  add_cmd ("delete", class_files, remote_delete_command,
+          _("Delete a remote file."),
+          &remote_cmdlist);
+
   /* Eventually initialize fileio.  See fileio.c */
   initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
 }
index 7ba4e49..f3ec8d7 100644 (file)
@@ -67,4 +67,10 @@ extern void (*deprecated_target_wait_loop_hook) (void);
 void register_remote_g_packet_guess (struct gdbarch *gdbarch, int bytes,
                                     const struct target_desc *tdesc);
 
+void remote_file_put (const char *local_file, const char *remote_file,
+                     int from_tty);
+void remote_file_get (const char *remote_file, const char *local_file,
+                     int from_tty);
+void remote_file_delete (const char *remote_file, int from_tty);
+
 #endif
index 6385c74..9c0f505 100644 (file)
@@ -1,3 +1,8 @@
+2007-11-30  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gdb.server/file-transfer.exp, gdb.server/transfer.txt,
+       gdb.mi/mi-file-transfer.exp: New.
+
 2007-11-25  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.dwarf2/dw2-ranges.S, gdb.dwarf2/dw2-ranges.exp: New files.
diff --git a/gdb/testsuite/gdb.mi/mi-file-transfer.exp b/gdb/testsuite/gdb.mi/mi-file-transfer.exp
new file mode 100644 (file)
index 0000000..3c06564
--- /dev/null
@@ -0,0 +1,99 @@
+# This testcase is part of GDB, the GNU debugger.
+# Copyright 2007 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test gdbserver monitor commands.
+
+load_lib gdbserver-support.exp
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+if { [skip_gdbserver_tests] } {
+    return 0
+}
+
+set testfile "basics"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested mi-file-transfer.exp
+    return -1
+}
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_file_cmd ${binfile}
+
+proc mi_gdbserver_run { } {
+    mi_gdb_test "kill" ".*" ""
+
+    set res [gdbserver_spawn ""]
+    set protocol [lindex $res 0]
+    set gdbport [lindex $res 1]
+
+    if { [mi_gdb_target_cmd $protocol $gdbport] != 0 } {
+       return -1
+    }
+
+    return 0
+}
+
+proc test_file_transfer { filename description } {
+    mi_gdb_test "-target-file-put \"$filename\" \"down-server\"" \
+       "\\^done" "put $description"
+    mi_gdb_test "-target-file-get \"down-server\" \"up-server\"" \
+       "\\^done" "get $description"
+
+    if { ![is_remote target] } {
+       # If we can check the target copy of the file, do that too.
+       # This should catch symmetric errors in upload and download.
+       set result [remote_exec host "cmp -s $filename down-server"]
+       if { [lindex $result 0] == 0 } {
+           pass "compare intermediate $description"
+       } else {
+           fail "compare intermediate $description"
+       }
+    }
+
+    set result [remote_exec host "cmp -s $filename up-server"]
+    if { [lindex $result 0] == 0 } {
+       pass "compare $description"
+    } else {
+       fail "compare $description"
+    }
+
+    mi_gdb_test "-target-file-delete \"down-server\"" \
+       "\\^done" "deleted $description"
+
+    if { ![is_remote target] } {
+       if { ! [remote_file target exists down-server] } {
+           pass "verified deleted $description"
+       } else {
+           fail "verified deleted $description"
+       }
+    }
+
+    catch { file delete up-server }
+}
+
+mi_gdbserver_run
+
+test_file_transfer "$binfile" "binary file"
+
+mi_gdb_exit
diff --git a/gdb/testsuite/gdb.server/file-transfer.exp b/gdb/testsuite/gdb.server/file-transfer.exp
new file mode 100644 (file)
index 0000000..6450a5d
--- /dev/null
@@ -0,0 +1,80 @@
+# This testcase is part of GDB, the GNU debugger.
+# Copyright 2007 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test gdbserver monitor commands.
+
+load_lib gdbserver-support.exp
+
+set testfile "server"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [skip_gdbserver_tests] } {
+    return 0
+}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested file-transfer.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load $binfile
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdbserver_run ""
+
+proc test_file_transfer { filename description } {
+    gdb_test "remote put \"$filename\" down-server" \
+       "Successfully sent .*" "put $description"
+    gdb_test "remote get down-server up-server" \
+       "Successfully fetched .*" "get $description"
+
+    if { ![is_remote target] } {
+       # If we can check the target copy of the file, do that too.
+       # This should catch symmetric errors in upload and download.
+       set result [remote_exec host "cmp -s $filename down-server"]
+       if { [lindex $result 0] == 0 } {
+           pass "compare intermediate $description"
+       } else {
+           fail "compare intermediate $description"
+       }
+    }
+
+    set result [remote_exec host "cmp -s $filename up-server"]
+    if { [lindex $result 0] == 0 } {
+       pass "compare $description"
+    } else {
+       fail "compare $description"
+    }
+
+    gdb_test "remote delete down-server" \
+       "Successfully deleted .*" "deleted $description"
+
+    if { ![is_remote target] } {
+       if { ! [remote_file target exists down-server] } {
+           pass "verified deleted $description"
+       } else {
+           fail "verified deleted $description"
+       }
+    }
+
+    catch { file delete up-server }
+}
+
+test_file_transfer "$binfile" "binary file"
+test_file_transfer "$srcdir/$subdir/transfer.txt" "text file"
diff --git a/gdb/testsuite/gdb.server/transfer.txt b/gdb/testsuite/gdb.server/transfer.txt
new file mode 100644 (file)
index 0000000..566f4c5
--- /dev/null
@@ -0,0 +1,12 @@
+This text file is a test input for GDB's file transfer commands.  It
+contains some characters which need to be escaped in remote protocol
+packets, like "*" and "}" and "$" and "#".  Actually, it contains
+a good sampling of printable characters:
+
+!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~
+
+!"#$%&'()*+,-./0123456789:;<=>?@
+ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
+abcdefghijklmnopqrstuvwxyz{|}~