diff options
author | Richard Jones <rjones@redhat.com> | 2010-04-19 14:41:01 +0100 |
---|---|---|
committer | Richard Jones <rjones@redhat.com> | 2010-04-19 15:08:45 +0100 |
commit | 3119aa687d4d594b50ee9df5ff6e8d709eb7ca85 (patch) | |
tree | f950fe7f4bce7b9795442b94e35afcef34159c66 /fish | |
parent | 819f30e332160ba05f7bf6dd068622cc07bf9ffc (diff) | |
download | libguestfs-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
Diffstat (limited to 'fish')
-rw-r--r-- | fish/fish.c | 141 | ||||
-rw-r--r-- | fish/fish.h | 3 | ||||
-rw-r--r-- | fish/guestfish.pod | 33 |
3 files changed, 177 insertions, 0 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 |