| /* |
| * Copyright 2015 Red Hat, Inc. |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| * |
| * Author: Matthias Clasen <mclasen@redhat.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <gio/gio.h> |
| #include <gi18n.h> |
| #include <stdio.h> |
| |
| #include "gio-tool.h" |
| |
| |
| /* statics {{{1 */ |
| |
| static gboolean no_target_directory = FALSE; |
| static gboolean progress = FALSE; |
| static gboolean interactive = FALSE; |
| static gboolean backup = FALSE; |
| static gboolean no_copy_fallback = FALSE; |
| |
| static const GOptionEntry entries[] = { |
| { "no-target-directory", 'T', 0, G_OPTION_ARG_NONE, &no_target_directory, N_("No target directory"), NULL }, |
| { "progress", 'p', 0, G_OPTION_ARG_NONE, &progress, N_("Show progress"), NULL }, |
| { "interactive", 'i', 0, G_OPTION_ARG_NONE, &interactive, N_("Prompt before overwrite"), NULL }, |
| { "backup", 'b', 0, G_OPTION_ARG_NONE, &backup, N_("Backup existing destination files"), NULL }, |
| { "no-copy-fallback", 'C', 0, G_OPTION_ARG_NONE, &no_copy_fallback, N_("Don’t use copy and delete fallback"), NULL }, |
| G_OPTION_ENTRY_NULL |
| }; |
| |
| static gint64 start_time; |
| static gint64 previous_time; |
| |
| static void |
| show_progress (goffset current_num_bytes, |
| goffset total_num_bytes, |
| gpointer user_data) |
| { |
| gint64 tv; |
| char *current_size, *total_size, *rate; |
| |
| tv = g_get_monotonic_time (); |
| if (tv - previous_time < (G_USEC_PER_SEC / 5) && |
| current_num_bytes != total_num_bytes) |
| return; |
| |
| current_size = g_format_size (current_num_bytes); |
| total_size = g_format_size (total_num_bytes); |
| rate = g_format_size (current_num_bytes / |
| MAX ((tv - start_time) / G_USEC_PER_SEC, 1)); |
| g_print ("\r\033[K"); |
| g_print (_("Transferred %s out of %s (%s/s)"), |
| current_size, total_size, rate); |
| |
| previous_time = tv; |
| |
| g_free (current_size); |
| g_free (total_size); |
| g_free (rate); |
| } |
| |
| int |
| handle_move (int argc, char *argv[], gboolean do_help) |
| { |
| GOptionContext *context; |
| gchar *param; |
| GError *error = NULL; |
| GFile *source, *dest, *target; |
| gboolean dest_is_dir; |
| char *basename; |
| char *uri; |
| int i; |
| GFileCopyFlags flags; |
| int retval = 0; |
| |
| g_set_prgname ("gio move"); |
| |
| /* Translators: commandline placeholder */ |
| param = g_strdup_printf ("%s… %s", _("SOURCE"), _("DESTINATION")); |
| context = g_option_context_new (param); |
| g_free (param); |
| g_option_context_set_help_enabled (context, FALSE); |
| g_option_context_set_summary (context, |
| _("Move one or more files from SOURCE to DEST.")); |
| g_option_context_set_description (context, |
| _("gio move is similar to the traditional mv utility, but using GIO\n" |
| "locations instead of local files: for example, you can use something\n" |
| "like smb://server/resource/file.txt as location")); |
| g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); |
| |
| if (do_help) |
| { |
| show_help (context, NULL); |
| g_option_context_free (context); |
| return 0; |
| } |
| |
| if (!g_option_context_parse (context, &argc, &argv, &error)) |
| { |
| show_help (context, error->message); |
| g_error_free (error); |
| g_option_context_free (context); |
| return 1; |
| } |
| |
| if (argc < 3) |
| { |
| show_help (context, NULL); |
| g_option_context_free (context); |
| return 1; |
| } |
| |
| dest = g_file_new_for_commandline_arg (argv[argc - 1]); |
| |
| if (no_target_directory && argc > 3) |
| { |
| show_help (context, NULL); |
| g_object_unref (dest); |
| g_option_context_free (context); |
| return 1; |
| } |
| |
| dest_is_dir = file_is_dir (dest); |
| |
| if (!dest_is_dir && argc > 3) |
| { |
| char *message; |
| message = g_strdup_printf (_("Target %s is not a directory"), argv[argc - 1]); |
| show_help (context, message); |
| g_free (message); |
| g_object_unref (dest); |
| g_option_context_free (context); |
| return 1; |
| } |
| |
| g_option_context_free (context); |
| |
| for (i = 1; i < argc - 1; i++) |
| { |
| source = g_file_new_for_commandline_arg (argv[i]); |
| |
| if (dest_is_dir && !no_target_directory) |
| { |
| basename = g_file_get_basename (source); |
| target = g_file_get_child (dest, basename); |
| g_free (basename); |
| } |
| else |
| target = g_object_ref (dest); |
| |
| flags = 0; |
| if (backup) |
| flags |= G_FILE_COPY_BACKUP; |
| if (!interactive) |
| flags |= G_FILE_COPY_OVERWRITE; |
| if (no_copy_fallback) |
| flags |= G_FILE_COPY_NO_FALLBACK_FOR_MOVE; |
| |
| error = NULL; |
| start_time = g_get_monotonic_time (); |
| if (!g_file_move (source, target, flags, NULL, progress ? show_progress : NULL, NULL, &error)) |
| { |
| if (interactive && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) |
| { |
| char line[16]; |
| |
| g_error_free (error); |
| error = NULL; |
| |
| uri = g_file_get_uri (target); |
| g_print (_("%s: overwrite “%s”? "), argv[0], uri); |
| g_free (uri); |
| if (fgets (line, sizeof (line), stdin) && |
| (line[0] == 'y' || line[0] == 'Y')) |
| { |
| flags |= G_FILE_COPY_OVERWRITE; |
| start_time = g_get_monotonic_time (); |
| if (!g_file_move (source, target, flags, NULL, progress ? show_progress : NULL, NULL, &error)) |
| goto move_failed; |
| } |
| } |
| else |
| { |
| move_failed: |
| print_file_error (source, error->message); |
| g_error_free (error); |
| retval = 1; |
| } |
| } |
| |
| if (progress && retval == 0) |
| g_print("\n"); |
| |
| g_object_unref (source); |
| g_object_unref (target); |
| } |
| |
| g_object_unref (dest); |
| |
| return retval; |
| } |