1 | /* |
2 | * Copyright 2015 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author: Matthias Clasen <mclasen@redhat.com> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <gio/gio.h> |
23 | #include <gi18n.h> |
24 | #include <stdio.h> |
25 | |
26 | #include "gio-tool.h" |
27 | |
28 | |
29 | /* statics {{{1 */ |
30 | |
31 | static gboolean no_target_directory = FALSE; |
32 | static gboolean progress = FALSE; |
33 | static gboolean interactive = FALSE; |
34 | static gboolean backup = FALSE; |
35 | static gboolean no_copy_fallback = FALSE; |
36 | |
37 | static const GOptionEntry entries[] = { |
38 | { "no-target-directory" , 'T', 0, G_OPTION_ARG_NONE, &no_target_directory, N_("No target directory" ), NULL }, |
39 | { "progress" , 'p', 0, G_OPTION_ARG_NONE, &progress, N_("Show progress" ), NULL }, |
40 | { "interactive" , 'i', 0, G_OPTION_ARG_NONE, &interactive, N_("Prompt before overwrite" ), NULL }, |
41 | { "backup" , 'b', 0, G_OPTION_ARG_NONE, &backup, N_("Backup existing destination files" ), NULL }, |
42 | { "no-copy-fallback" , 'C', 0, G_OPTION_ARG_NONE, &no_copy_fallback, N_("Don’t use copy and delete fallback" ), NULL }, |
43 | { NULL } |
44 | }; |
45 | |
46 | static gint64 start_time; |
47 | static gint64 previous_time; |
48 | |
49 | static void |
50 | show_progress (goffset current_num_bytes, |
51 | goffset total_num_bytes, |
52 | gpointer user_data) |
53 | { |
54 | gint64 tv; |
55 | char *current_size, *total_size, *rate; |
56 | |
57 | tv = g_get_monotonic_time (); |
58 | if (tv - previous_time < (G_USEC_PER_SEC / 5) && |
59 | current_num_bytes != total_num_bytes) |
60 | return; |
61 | |
62 | current_size = g_format_size (size: current_num_bytes); |
63 | total_size = g_format_size (size: total_num_bytes); |
64 | rate = g_format_size (size: current_num_bytes / |
65 | MAX ((tv - start_time) / G_USEC_PER_SEC, 1)); |
66 | g_print (format: "\r\033[K" ); |
67 | g_print (_("Transferred %s out of %s (%s/s)" ), |
68 | current_size, total_size, rate); |
69 | |
70 | previous_time = tv; |
71 | |
72 | g_free (mem: current_size); |
73 | g_free (mem: total_size); |
74 | g_free (mem: rate); |
75 | } |
76 | |
77 | int |
78 | handle_move (int argc, char *argv[], gboolean do_help) |
79 | { |
80 | GOptionContext *context; |
81 | gchar *param; |
82 | GError *error = NULL; |
83 | GFile *source, *dest, *target; |
84 | gboolean dest_is_dir; |
85 | char *basename; |
86 | char *uri; |
87 | int i; |
88 | GFileCopyFlags flags; |
89 | int retval = 0; |
90 | |
91 | g_set_prgname (prgname: "gio move" ); |
92 | |
93 | /* Translators: commandline placeholder */ |
94 | param = g_strdup_printf (format: "%s… %s" , _("SOURCE" ), _("DESTINATION" )); |
95 | context = g_option_context_new (parameter_string: param); |
96 | g_free (mem: param); |
97 | g_option_context_set_help_enabled (context, FALSE); |
98 | g_option_context_set_summary (context, |
99 | _("Move one or more files from SOURCE to DEST." )); |
100 | g_option_context_set_description (context, |
101 | _("gio move is similar to the traditional mv utility, but using GIO\n" |
102 | "locations instead of local files: for example, you can use something\n" |
103 | "like smb://server/resource/file.txt as location" )); |
104 | g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); |
105 | |
106 | if (do_help) |
107 | { |
108 | show_help (context, NULL); |
109 | g_option_context_free (context); |
110 | return 0; |
111 | } |
112 | |
113 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
114 | { |
115 | show_help (context, message: error->message); |
116 | g_error_free (error); |
117 | g_option_context_free (context); |
118 | return 1; |
119 | } |
120 | |
121 | if (argc < 3) |
122 | { |
123 | show_help (context, NULL); |
124 | g_option_context_free (context); |
125 | return 1; |
126 | } |
127 | |
128 | dest = g_file_new_for_commandline_arg (arg: argv[argc - 1]); |
129 | |
130 | if (no_target_directory && argc > 3) |
131 | { |
132 | show_help (context, NULL); |
133 | g_object_unref (object: dest); |
134 | g_option_context_free (context); |
135 | return 1; |
136 | } |
137 | |
138 | dest_is_dir = file_is_dir (file: dest); |
139 | |
140 | if (!dest_is_dir && argc > 3) |
141 | { |
142 | char *message; |
143 | message = g_strdup_printf (_("Target %s is not a directory" ), argv[argc - 1]); |
144 | show_help (context, message); |
145 | g_free (mem: message); |
146 | g_object_unref (object: dest); |
147 | g_option_context_free (context); |
148 | return 1; |
149 | } |
150 | |
151 | g_option_context_free (context); |
152 | |
153 | for (i = 1; i < argc - 1; i++) |
154 | { |
155 | source = g_file_new_for_commandline_arg (arg: argv[i]); |
156 | |
157 | if (dest_is_dir && !no_target_directory) |
158 | { |
159 | basename = g_file_get_basename (file: source); |
160 | target = g_file_get_child (file: dest, name: basename); |
161 | g_free (mem: basename); |
162 | } |
163 | else |
164 | target = g_object_ref (dest); |
165 | |
166 | flags = 0; |
167 | if (backup) |
168 | flags |= G_FILE_COPY_BACKUP; |
169 | if (!interactive) |
170 | flags |= G_FILE_COPY_OVERWRITE; |
171 | if (no_copy_fallback) |
172 | flags |= G_FILE_COPY_NO_FALLBACK_FOR_MOVE; |
173 | |
174 | error = NULL; |
175 | start_time = g_get_monotonic_time (); |
176 | if (!g_file_move (source, destination: target, flags, NULL, progress_callback: progress ? show_progress : NULL, NULL, error: &error)) |
177 | { |
178 | if (interactive && g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_EXISTS)) |
179 | { |
180 | char line[16]; |
181 | |
182 | g_error_free (error); |
183 | error = NULL; |
184 | |
185 | uri = g_file_get_uri (file: target); |
186 | g_print (_("%s: overwrite “%s”? " ), argv[0], uri); |
187 | g_free (mem: uri); |
188 | if (fgets (s: line, n: sizeof (line), stdin) && |
189 | (line[0] == 'y' || line[0] == 'Y')) |
190 | { |
191 | flags |= G_FILE_COPY_OVERWRITE; |
192 | start_time = g_get_monotonic_time (); |
193 | if (!g_file_move (source, destination: target, flags, NULL, progress_callback: progress ? show_progress : NULL, NULL, error: &error)) |
194 | goto move_failed; |
195 | } |
196 | } |
197 | else |
198 | { |
199 | move_failed: |
200 | print_file_error (file: source, message: error->message); |
201 | g_error_free (error); |
202 | retval = 1; |
203 | } |
204 | } |
205 | |
206 | if (progress && retval == 0) |
207 | g_print(format: "\n" ); |
208 | |
209 | g_object_unref (object: source); |
210 | g_object_unref (object: target); |
211 | } |
212 | |
213 | g_object_unref (object: dest); |
214 | |
215 | return retval; |
216 | } |
217 | |