+
+/* Resolve the special "win:..." form for Windows-specific paths.
+ * This always returns a newly allocated string which is freed by the
+ * caller function in "cmds.c".
+ */
+char *
+resolve_win_path (const char *path)
+{
+ char *ret;
+ size_t i;
+
+ if (STRCASENEQLEN (path, "win:", 4)) {
+ ret = strdup (path);
+ if (ret == NULL)
+ perror ("strdup");
+ return ret;
+ }
+
+ path += 4;
+
+ /* Drop drive letter, if it's "C:". */
+ if (STRCASEEQLEN (path, "c:", 2))
+ path += 2;
+
+ if (!*path) {
+ ret = strdup ("/");
+ if (ret == NULL)
+ perror ("strdup");
+ return ret;
+ }
+
+ ret = strdup (path);
+ if (ret == NULL) {
+ perror ("strdup");
+ return NULL;
+ }
+
+ /* Blindly convert any backslashes into forward slashes. Is this good? */
+ for (i = 0; i < strlen (ret); ++i)
+ if (ret[i] == '\\')
+ ret[i] = '/';
+
+ char *t = guestfs_case_sensitive_path (g, ret);
+ free (ret);
+ ret = t;
+
+ 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)
+{
+ TMP_TEMPLATE_ON_STACK (template);
+ 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;
+}
+
+/* Read a passphrase ('Key') from /dev/tty with echo off.
+ * The caller (cmds.c) will call free on the string afterwards.
+ * Based on the code in cryptsetup file lib/utils.c.
+ */
+char *
+read_key (const char *param)
+{
+ FILE *infp, *outfp;
+ struct termios orig, temp;
+ char *ret = NULL;
+
+ /* Read and write to /dev/tty if available. */
+ if (keys_from_stdin ||
+ (infp = outfp = fopen ("/dev/tty", "w+")) == NULL) {
+ infp = stdin;
+ outfp = stdout;
+ }
+
+ /* Print the prompt and set no echo. */
+ int tty = isatty (fileno (infp));
+ int tcset = 0;
+ if (tty) {
+ fprintf (outfp, _("Enter key or passphrase (\"%s\"): "), param);
+
+ if (!echo_keys) {
+ if (tcgetattr (fileno (infp), &orig) == -1) {
+ perror ("tcgetattr");
+ goto error;
+ }
+ memcpy (&temp, &orig, sizeof temp);
+ temp.c_lflag &= ~ECHO;
+
+ tcsetattr (fileno (infp), TCSAFLUSH, &temp);
+ tcset = 1;
+ }
+ }
+
+ size_t n = 0;
+ ssize_t len;
+ len = getline (&ret, &n, infp);
+ if (len == -1) {
+ perror ("getline");
+ ret = NULL;
+ goto error;
+ }
+
+ /* Remove the terminating \n if there is one. */
+ if (len > 0 && ret[len-1] == '\n')
+ ret[len-1] = '\0';
+
+ error:
+ /* Restore echo, close file descriptor. */
+ if (tty && tcset) {
+ printf ("\n");
+ tcsetattr (fileno (infp), TCSAFLUSH, &orig);
+ }
+
+ if (infp != stdin)
+ fclose (infp); /* outfp == infp, so this is closed also */
+
+ return ret;
+}