summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2010-04-19 14:41:01 +0100
committerRichard Jones <rjones@redhat.com>2010-04-19 15:08:45 +0100
commit3119aa687d4d594b50ee9df5ff6e8d709eb7ca85 (patch)
treef950fe7f4bce7b9795442b94e35afcef34159c66
parent819f30e332160ba05f7bf6dd068622cc07bf9ffc (diff)
downloadlibguestfs-3119aa687d4d594b50ee9df5ff6e8d709eb7ca85.tar.gz
libguestfs-3119aa687d4d594b50ee9df5ff6e8d709eb7ca85.tar.xz
libguestfs-3119aa687d4d594b50ee9df5ff6e8d709eb7ca85.zip
fish: Allow -<<END as a syntax for uploading "heredocs".
For example: ><fs> upload -<<END /foo some data some more data END ><fs> cat /foo some data some more data
-rw-r--r--fish/fish.c141
-rw-r--r--fish/fish.h3
-rw-r--r--fish/guestfish.pod33
-rwxr-xr-xsrc/generator.ml22
4 files changed, 189 insertions, 10 deletions
diff --git a/fish/fish.c b/fish/fish.c
index 61a84050..db3149e1 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -1419,3 +1419,144 @@ resolve_win_path (const char *path)
return ret;
}
+
+/* Resolve the special FileIn paths ("-" or "-<<END" or filename).
+ * The caller (cmds.c) will call free_file_in after the command has
+ * run which should clean up resources.
+ */
+static char *file_in_heredoc (const char *endmarker);
+static char *file_in_tmpfile = NULL;
+
+char *
+file_in (const char *arg)
+{
+ char *ret;
+
+ if (STREQ (arg, "-")) {
+ ret = strdup ("/dev/stdin");
+ if (!ret) {
+ perror ("strdup");
+ return NULL;
+ }
+ }
+ else if (STRPREFIX (arg, "-<<")) {
+ const char *endmarker = &arg[3];
+ if (*endmarker == '\0') {
+ fprintf (stderr, "%s: missing end marker in -<< expression\n",
+ program_name);
+ return NULL;
+ }
+ ret = file_in_heredoc (endmarker);
+ if (ret == NULL)
+ return NULL;
+ }
+ else {
+ ret = strdup (arg);
+ if (!ret) {
+ perror ("strdup");
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+static char *
+file_in_heredoc (const char *endmarker)
+{
+ static const char template[] = "/tmp/heredocXXXXXX";
+ file_in_tmpfile = strdup (template);
+ if (file_in_tmpfile == NULL) {
+ perror ("strdup");
+ return NULL;
+ }
+
+ int fd = mkstemp (file_in_tmpfile);
+ if (fd == -1) {
+ perror ("mkstemp");
+ goto error1;
+ }
+
+ size_t markerlen = strlen (endmarker);
+
+ char buffer[BUFSIZ];
+ int write_error = 0;
+ while (fgets (buffer, sizeof buffer, stdin) != NULL) {
+ /* Look for "END"<EOF> or "END\n" in input. */
+ size_t blen = strlen (buffer);
+ if (STREQLEN (buffer, endmarker, markerlen) &&
+ (blen == markerlen ||
+ (blen == markerlen+1 && buffer[markerlen] == '\n')))
+ goto found_end;
+
+ if (xwrite (fd, buffer, blen) == -1) {
+ if (!write_error) perror ("write");
+ write_error = 1;
+ /* continue reading up to the end marker */
+ }
+ }
+
+ /* Reached EOF of stdin without finding the end marker, which
+ * is likely to be an error.
+ */
+ fprintf (stderr, "%s: end of input reached without finding '%s'\n",
+ program_name, endmarker);
+ goto error2;
+
+ found_end:
+ if (write_error) {
+ close (fd);
+ goto error2;
+ }
+
+ if (close (fd) == -1) {
+ perror ("close");
+ goto error2;
+ }
+
+ return file_in_tmpfile;
+
+ error2:
+ unlink (file_in_tmpfile);
+
+ error1:
+ free (file_in_tmpfile);
+ file_in_tmpfile = NULL;
+ return NULL;
+}
+
+void
+free_file_in (char *s)
+{
+ if (file_in_tmpfile) {
+ if (unlink (file_in_tmpfile) == -1)
+ perror (file_in_tmpfile);
+ file_in_tmpfile = NULL;
+ }
+
+ /* Free the device or file name which was strdup'd in file_in().
+ * Note it's not immediately clear, but for -<< heredocs,
+ * s == file_in_tmpfile, so this frees up that buffer.
+ */
+ free (s);
+}
+
+/* Resolve the special FileOut paths ("-" or filename).
+ * The caller (cmds.c) will call free (str) after the command has run.
+ */
+char *
+file_out (const char *arg)
+{
+ char *ret;
+
+ if (STREQ (arg, "-"))
+ ret = strdup ("/dev/stdout");
+ else
+ ret = strdup (arg);
+
+ if (!ret) {
+ perror ("strdup");
+ return NULL;
+ }
+ return ret;
+}
diff --git a/fish/fish.h b/fish/fish.h
index 5856b8ef..05135fba 100644
--- a/fish/fish.h
+++ b/fish/fish.h
@@ -65,6 +65,9 @@ extern int is_true (const char *str);
extern char **parse_string_list (const char *str);
extern int xwrite (int fd, const void *buf, size_t len);
extern char *resolve_win_path (const char *path);
+extern char *file_in (const char *arg);
+extern void free_file_in (char *s);
+extern char *file_out (const char *arg);
extern void extended_help_message (void);
/* in cmds.c (auto-generated) */
diff --git a/fish/guestfish.pod b/fish/guestfish.pod
index e61d4e5b..836c4f7f 100644
--- a/fish/guestfish.pod
+++ b/fish/guestfish.pod
@@ -486,6 +486,39 @@ user ID of the process, and C<$PID> is the process ID of the server.
Guestfish client and server versions must match exactly.
+=head1 UPLOADING AND DOWNLOADING FILES
+
+For commands such as C<upload>, C<download>, C<tar-in>, C<tar-out> and
+others which upload from or download to a local file, you can use the
+special filename C<-> to mean "from stdin" or "to stdout". For example:
+
+ upload - /foo
+
+reads stdin and creates from that a file C</foo> in the disk image,
+and:
+
+ tar-out /etc - | tar tf -
+
+writes the tarball to stdout and then pipes that into the external
+"tar" command (see L</PIPES>).
+
+When using C<-> to read from stdin, the input is read up to the end of
+stdin. You can also use a special "heredoc"-like syntax to read up to
+some arbitrary end marker:
+
+ upload -<<END /foo
+ input line 1
+ input line 2
+ input line 3
+ END
+
+Any string of characters can be used instead of C<END>. The end
+marker must appear on a line of its own, without any preceeding or
+following characters (not even spaces).
+
+Note that the C<-E<lt>E<lt>> syntax only applies to parameters used to
+upload local files (so-called "FileIn" parameters in the generator).
+
=head1 GUESTFISH COMMANDS
The commands in this section are guestfish convenience commands, in
diff --git a/src/generator.ml b/src/generator.ml
index 65efd665..580cb143 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -7385,11 +7385,11 @@ and generate_fish_cmds () =
function
| Device n
| String n
- | OptString n
- | FileIn n
- | FileOut n -> pr " const char *%s;\n" n
+ | OptString n -> pr " const char *%s;\n" n
| Pathname n
- | Dev_or_Path n -> pr " char *%s;\n" n
+ | Dev_or_Path n
+ | FileIn n
+ | FileOut n -> pr " char *%s;\n" n
| StringList n | DeviceList n -> pr " char **%s;\n" n
| Bool n -> pr " int %s;\n" n
| Int n -> pr " int %s;\n" n
@@ -7446,11 +7446,11 @@ and generate_fish_cmds () =
pr " %s = STRNEQ (argv[%d], \"\") ? argv[%d] : NULL;\n"
name i i
| FileIn name ->
- pr " %s = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdin\";\n"
- name i i
+ pr " %s = file_in (argv[%d]);\n" name i;
+ pr " if (%s == NULL) return -1;\n" name
| FileOut name ->
- pr " %s = STRNEQ (argv[%d], \"-\") ? argv[%d] : \"/dev/stdout\";\n"
- name i i
+ pr " %s = file_out (argv[%d]);\n" name i;
+ pr " if (%s == NULL) return -1;\n" name
| StringList name | DeviceList name ->
pr " %s = parse_string_list (argv[%d]);\n" name i;
pr " if (%s == NULL) return -1;\n" name;
@@ -7479,10 +7479,12 @@ and generate_fish_cmds () =
List.iter (
function
| Device name | String name
- | OptString name | FileIn name | FileOut name | Bool name
+ | OptString name | Bool name
| Int name | Int64 name -> ()
- | Pathname name | Dev_or_Path name ->
+ | Pathname name | Dev_or_Path name | FileOut name ->
pr " free (%s);\n" name
+ | FileIn name ->
+ pr " free_file_in (%s);\n" name
| StringList name | DeviceList name ->
pr " free_strings (%s);\n" name
) (snd style);