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 | #if 0 |
26 | #include <locale.h> |
27 | #include <string.h> |
28 | #include <stdlib.h> |
29 | #include <errno.h> |
30 | #endif |
31 | |
32 | #include "gio-tool.h" |
33 | |
34 | static gboolean no_target_directory = FALSE; |
35 | static gboolean progress = FALSE; |
36 | static gboolean interactive = FALSE; |
37 | static gboolean preserve = FALSE; |
38 | static gboolean backup = FALSE; |
39 | static gboolean no_dereference = FALSE; |
40 | static gboolean default_permissions = FALSE; |
41 | |
42 | static const GOptionEntry entries[] = { |
43 | { "no-target-directory" , 'T', 0, G_OPTION_ARG_NONE, &no_target_directory, N_("No target directory" ), NULL }, |
44 | { "progress" , 'p', 0, G_OPTION_ARG_NONE, &progress, N_("Show progress" ), NULL }, |
45 | { "interactive" , 'i', 0, G_OPTION_ARG_NONE, &interactive, N_("Prompt before overwrite" ), NULL }, |
46 | { "preserve" , 'p', 0, G_OPTION_ARG_NONE, &preserve, N_("Preserve all attributes" ), NULL }, |
47 | { "backup" , 'b', 0, G_OPTION_ARG_NONE, &backup, N_("Backup existing destination files" ), NULL }, |
48 | { "no-dereference" , 'P', 0, G_OPTION_ARG_NONE, &no_dereference, N_("Never follow symbolic links" ), NULL }, |
49 | { "default-permissions" , 0, 0, G_OPTION_ARG_NONE, &default_permissions, N_("Use default permissions for the destination" ), NULL }, |
50 | { NULL } |
51 | }; |
52 | |
53 | static gint64 start_time; |
54 | static gint64 previous_time; |
55 | |
56 | static void |
57 | show_progress (goffset current_num_bytes, |
58 | goffset total_num_bytes, |
59 | gpointer user_data) |
60 | { |
61 | gint64 tv; |
62 | char *current_size, *total_size, *rate; |
63 | |
64 | tv = g_get_monotonic_time (); |
65 | if (tv - previous_time < (G_USEC_PER_SEC / 5) && |
66 | current_num_bytes != total_num_bytes) |
67 | return; |
68 | |
69 | current_size = g_format_size (size: current_num_bytes); |
70 | total_size = g_format_size (size: total_num_bytes); |
71 | rate = g_format_size (size: current_num_bytes / |
72 | MAX ((tv - start_time) / G_USEC_PER_SEC, 1)); |
73 | g_print (format: "\r\033[K" ); |
74 | g_print (_("Transferred %s out of %s (%s/s)" ), current_size, total_size, rate); |
75 | |
76 | previous_time = tv; |
77 | |
78 | g_free (mem: current_size); |
79 | g_free (mem: total_size); |
80 | g_free (mem: rate); |
81 | } |
82 | |
83 | int |
84 | handle_copy (int argc, char *argv[], gboolean do_help) |
85 | { |
86 | GOptionContext *context; |
87 | GError *error = NULL; |
88 | char *param; |
89 | GFile *source, *dest, *target; |
90 | gboolean dest_is_dir; |
91 | char *basename; |
92 | char *uri; |
93 | int i; |
94 | GFileCopyFlags flags; |
95 | int retval = 0; |
96 | |
97 | g_set_prgname (prgname: "gio copy" ); |
98 | |
99 | /* Translators: commandline placeholder */ |
100 | param = g_strdup_printf (format: "%s… %s" , _("SOURCE" ), _("DESTINATION" )); |
101 | context = g_option_context_new (parameter_string: param); |
102 | g_free (mem: param); |
103 | g_option_context_set_help_enabled (context, FALSE); |
104 | g_option_context_set_summary (context, |
105 | _("Copy one or more files from SOURCE to DESTINATION." )); |
106 | g_option_context_set_description (context, |
107 | _("gio copy is similar to the traditional cp utility, but using GIO\n" |
108 | "locations instead of local files: for example, you can use something\n" |
109 | "like smb://server/resource/file.txt as location." )); |
110 | g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); |
111 | |
112 | if (do_help) |
113 | { |
114 | show_help (context, NULL); |
115 | g_option_context_free (context); |
116 | return 0; |
117 | } |
118 | |
119 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
120 | { |
121 | show_help (context, message: error->message); |
122 | g_error_free (error); |
123 | g_option_context_free (context); |
124 | return 1; |
125 | } |
126 | |
127 | if (argc < 3) |
128 | { |
129 | show_help (context, NULL); |
130 | g_option_context_free (context); |
131 | return 1; |
132 | } |
133 | |
134 | dest = g_file_new_for_commandline_arg (arg: argv[argc - 1]); |
135 | |
136 | if (no_target_directory && argc > 3) |
137 | { |
138 | show_help (context, NULL); |
139 | g_object_unref (object: dest); |
140 | g_option_context_free (context); |
141 | return 1; |
142 | } |
143 | |
144 | dest_is_dir = file_is_dir (file: dest); |
145 | if (!dest_is_dir && argc > 3) |
146 | { |
147 | char *message; |
148 | |
149 | message = g_strdup_printf (_("Destination %s is not a directory" ), argv[argc - 1]); |
150 | show_help (context, message); |
151 | g_free (mem: message); |
152 | g_object_unref (object: dest); |
153 | g_option_context_free (context); |
154 | return 1; |
155 | } |
156 | |
157 | g_option_context_free (context); |
158 | |
159 | for (i = 1; i < argc - 1; i++) |
160 | { |
161 | source = g_file_new_for_commandline_arg (arg: argv[i]); |
162 | if (dest_is_dir && !no_target_directory) |
163 | { |
164 | basename = g_file_get_basename (file: source); |
165 | target = g_file_get_child (file: dest, name: basename); |
166 | g_free (mem: basename); |
167 | } |
168 | else |
169 | target = g_object_ref (dest); |
170 | |
171 | flags = 0; |
172 | if (backup) |
173 | flags |= G_FILE_COPY_BACKUP; |
174 | if (!interactive) |
175 | flags |= G_FILE_COPY_OVERWRITE; |
176 | if (no_dereference) |
177 | flags |= G_FILE_COPY_NOFOLLOW_SYMLINKS; |
178 | if (preserve) |
179 | flags |= G_FILE_COPY_ALL_METADATA; |
180 | if (default_permissions) |
181 | flags |= G_FILE_COPY_TARGET_DEFAULT_PERMS; |
182 | |
183 | error = NULL; |
184 | start_time = g_get_monotonic_time (); |
185 | |
186 | if (!g_file_copy (source, destination: target, flags, NULL, progress_callback: progress ? show_progress : NULL, NULL, error: &error)) |
187 | { |
188 | if (interactive && g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_EXISTS)) |
189 | { |
190 | char line[16]; |
191 | |
192 | g_error_free (error); |
193 | error = NULL; |
194 | |
195 | uri = g_file_get_uri (file: target); |
196 | g_print (_("%s: overwrite “%s”? " ), argv[0], uri); |
197 | g_free (mem: uri); |
198 | |
199 | if (fgets (s: line, n: sizeof (line), stdin) && |
200 | (line[0] == 'y' || line[0] == 'Y')) |
201 | { |
202 | flags |= G_FILE_COPY_OVERWRITE; |
203 | start_time = g_get_monotonic_time (); |
204 | if (!g_file_copy (source, destination: target, flags, NULL, progress_callback: progress ? show_progress : NULL, NULL, error: &error)) |
205 | goto copy_failed; |
206 | } |
207 | } |
208 | else |
209 | { |
210 | copy_failed: |
211 | print_file_error (file: source, message: error->message); |
212 | g_error_free (error); |
213 | retval = 1; |
214 | } |
215 | } |
216 | |
217 | if (progress && retval == 0) |
218 | g_print (format: "\n" ); |
219 | |
220 | g_object_unref (object: source); |
221 | g_object_unref (object: target); |
222 | } |
223 | |
224 | g_object_unref (object: dest); |
225 | |
226 | return retval; |
227 | } |
228 | |