source: mainline/uspace/app/bdsh/cmds/modules/cp/cp.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 12.6 KB
Line 
1/*
2 * Copyright (c) 2008 Tim Post
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <errno.h>
30#include <str_error.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <io/console.h>
34#include <io/keycode.h>
35#include <getopt.h>
36#include <str.h>
37#include <vfs/vfs.h>
38#include <dirent.h>
39#include "config.h"
40#include "util.h"
41#include "errors.h"
42#include "entry.h"
43#include "cp.h"
44#include "cmds.h"
45
46#define CP_VERSION "0.0.1"
47#define CP_DEFAULT_BUFLEN 1024
48
49static const char *cmdname = "cp";
50static console_ctrl_t *con;
51
52static struct option const long_options[] = {
53 { "buffer", required_argument, 0, 'b' },
54 { "force", no_argument, 0, 'f' },
55 { "interactive", no_argument, 0, 'i'},
56 { "recursive", no_argument, 0, 'r' },
57 { "help", no_argument, 0, 'h' },
58 { "version", no_argument, 0, 'v' },
59 { "verbose", no_argument, 0, 'V' },
60 { 0, 0, 0, 0 }
61};
62
63typedef enum {
64 TYPE_NONE,
65 TYPE_FILE,
66 TYPE_DIR
67} dentry_type_t;
68
69static int copy_file(const char *src, const char *dest,
70 size_t blen, int vb);
71
72/** Get the type of a directory entry.
73 *
74 * @param path Path of the directory entry.
75 *
76 * @return TYPE_DIR if the dentry is a directory.
77 * @return TYPE_FILE if the dentry is a file.
78 * @return TYPE_NONE if the dentry does not exists.
79 */
80static dentry_type_t get_type(const char *path)
81{
82 vfs_stat_t s;
83
84 errno_t r = vfs_stat_path(path, &s);
85
86 if (r != EOK)
87 return TYPE_NONE;
88 else if (s.is_directory)
89 return TYPE_DIR;
90 else if (s.is_file)
91 return TYPE_FILE;
92
93 return TYPE_NONE;
94}
95
96static int strtoint(const char *s1)
97{
98 long t1;
99
100 if (-1 == (t1 = strtol(s1, (char **) NULL, 10)))
101 return -1;
102
103 if (t1 <= 0)
104 return -1;
105
106 return (int) t1;
107}
108
109/** Get the last component of a path.
110 *
111 * e.g. /data/a ---> a
112 *
113 * @param path Pointer to the path.
114 *
115 * @return Pointer to the last component or to the path itself.
116 */
117static char *get_last_path_component(char *path)
118{
119 char *ptr;
120
121 ptr = str_rchr(path, '/');
122 if (!ptr)
123 return path;
124 else
125 return ptr + 1;
126}
127
128/** Merge two paths together.
129 *
130 * e.g. (path1 = /data/dir, path2 = a/b) --> /data/dir/a/b
131 *
132 * @param path1 Path to which path2 will be appended.
133 * @param path1_size Size of the path1 buffer.
134 * @param path2 Path that will be appended to path1.
135 */
136static void merge_paths(char *path1, size_t path1_size, char *path2)
137{
138 const char *delim = "/";
139
140 str_rtrim(path1, '/');
141 str_append(path1, path1_size, delim);
142 str_append(path1, path1_size, path2);
143}
144
145static bool get_user_decision(bool bdefault, const char *message, ...)
146{
147 va_list args;
148
149 va_start(args, message);
150 vprintf(message, args);
151 va_end(args);
152
153 while (true) {
154 cons_event_t ev;
155 console_flush(con);
156 console_get_event(con, &ev);
157 if (ev.type != CEV_KEY || ev.ev.key.type != KEY_PRESS ||
158 (ev.ev.key.mods & (KM_CTRL | KM_ALT)) != 0) {
159 continue;
160 }
161
162 switch(ev.ev.key.key) {
163 case KC_Y:
164 printf("y\n");
165 return true;
166 case KC_N:
167 printf("n\n");
168 return false;
169 case KC_ENTER:
170 printf("%c\n", bdefault ? 'Y' : 'N');
171 return bdefault;
172 default:
173 break;
174 }
175 }
176}
177
178static errno_t do_copy(const char *src, const char *dest,
179 size_t blen, int vb, int recursive, int force, int interactive)
180{
181 errno_t rc = EOK;
182 char dest_path[PATH_MAX];
183 char src_path[PATH_MAX];
184 DIR *dir = NULL;
185 struct dirent *dp;
186
187 dentry_type_t src_type = get_type(src);
188 dentry_type_t dest_type = get_type(dest);
189
190 const size_t src_len = str_size(src);
191
192 if (src_type == TYPE_FILE) {
193 char *src_fname;
194
195 /* Initialize the src_path with the src argument */
196 str_cpy(src_path, src_len + 1, src);
197 str_rtrim(src_path, '/');
198
199 /* Get the last component name from the src path */
200 src_fname = get_last_path_component(src_path);
201
202 /* Initialize dest_path with the dest argument */
203 str_cpy(dest_path, PATH_MAX, dest);
204
205 if (dest_type == TYPE_DIR) {
206 /* e.g. cp file_name /data */
207 /* e.g. cp file_name /data/ */
208
209 /* dest is a directory,
210 * append the src filename to it.
211 */
212 merge_paths(dest_path, PATH_MAX, src_fname);
213 dest_type = get_type(dest_path);
214 } else if (dest_type == TYPE_NONE) {
215 if (dest_path[str_size(dest_path) - 1] == '/') {
216 /* e.g. cp /textdemo /data/dirnotexists/ */
217
218 printf("The dest directory %s does not exists",
219 dest_path);
220 rc = ENOENT;
221 goto exit;
222 }
223 }
224
225 if (dest_type == TYPE_DIR) {
226 printf("Cannot overwrite existing directory %s\n",
227 dest_path);
228 rc = EEXIST;
229 goto exit;
230 } else if (dest_type == TYPE_FILE) {
231 /* e.g. cp file_name existing_file */
232
233 /* dest already exists,
234 * if force is set we will try to remove it.
235 * if interactive is set user input is required.
236 */
237 if (force && !interactive) {
238 rc = vfs_unlink_path(dest_path);
239 if (rc != EOK) {
240 printf("Unable to remove %s\n",
241 dest_path);
242 goto exit;
243 }
244 } else if (!force && interactive) {
245 bool overwrite = get_user_decision(false,
246 "File already exists: %s. Overwrite? [y/N]: ",
247 dest_path);
248 if (overwrite) {
249 printf("Overwriting file: %s\n", dest_path);
250 rc = vfs_unlink_path(dest_path);
251 if (rc != EOK) {
252 printf("Unable to remove %s\n", dest_path);
253 goto exit;
254 }
255 } else {
256 printf("Not overwriting file: %s\n", dest_path);
257 rc = EOK;
258 goto exit;
259 }
260 } else {
261 printf("File already exists: %s\n", dest_path);
262 rc = EEXIST;
263 goto exit;
264 }
265 }
266
267 /* call copy_file and exit */
268 if (copy_file(src, dest_path, blen, vb) < 0) {
269 rc = EIO;
270 }
271
272 } else if (src_type == TYPE_DIR) {
273 /* e.g. cp -r /x/srcdir /y/destdir/ */
274
275 if (!recursive) {
276 printf("Cannot copy the %s directory without the "
277 "-r option\n", src);
278 rc = EINVAL;
279 goto exit;
280 } else if (dest_type == TYPE_FILE) {
281 printf("Cannot overwrite a file with a directory\n");
282 rc = EEXIST;
283 goto exit;
284 }
285
286 char *src_dirname;
287
288 /* Initialize src_path with the content of src */
289 str_cpy(src_path, src_len + 1, src);
290 str_rtrim(src_path, '/');
291
292 src_dirname = get_last_path_component(src_path);
293
294 str_cpy(dest_path, PATH_MAX, dest);
295
296 switch (dest_type) {
297 case TYPE_DIR:
298 if (str_cmp(src_dirname, "..") &&
299 str_cmp(src_dirname, ".")) {
300 /* The last component of src_path is
301 * not '.' or '..'
302 */
303 merge_paths(dest_path, PATH_MAX, src_dirname);
304
305 rc = vfs_link_path(dest_path, KIND_DIRECTORY,
306 NULL);
307 if (rc != EOK) {
308 printf("Unable to create "
309 "dest directory %s\n", dest_path);
310 goto exit;
311 }
312 }
313 break;
314 default:
315 case TYPE_NONE:
316 /* dest does not exists, this means the user wants
317 * to specify the name of the destination directory
318 *
319 * e.g. cp -r /src /data/new_dir_src
320 */
321 rc = vfs_link_path(dest_path, KIND_DIRECTORY, NULL);
322 if (rc != EOK) {
323 printf("Unable to create "
324 "dest directory %s\n", dest_path);
325 goto exit;
326 }
327 break;
328 }
329
330 dir = opendir(src);
331 if (!dir) {
332 /* Something strange is happening... */
333 printf("Unable to open src %s directory\n", src);
334 rc = ENOENT;
335 goto exit;
336 }
337
338 /* Copy every single directory entry of src into the
339 * destination directory.
340 */
341 while ((dp = readdir(dir))) {
342 vfs_stat_t src_s;
343 vfs_stat_t dest_s;
344
345 char src_dent[PATH_MAX];
346 char dest_dent[PATH_MAX];
347
348 str_cpy(src_dent, PATH_MAX, src);
349 merge_paths(src_dent, PATH_MAX, dp->d_name);
350
351 str_cpy(dest_dent, PATH_MAX, dest_path);
352 merge_paths(dest_dent, PATH_MAX, dp->d_name);
353
354 /* Check if we are copying a directory into itself */
355 vfs_stat_path(src_dent, &src_s);
356 vfs_stat_path(dest_path, &dest_s);
357
358 if (dest_s.index == src_s.index &&
359 dest_s.fs_handle == src_s.fs_handle) {
360 printf("Cannot copy a directory "
361 "into itself\n");
362 rc = EEXIST;
363 goto exit;
364 }
365
366 if (vb)
367 printf("copy %s %s\n", src_dent, dest_dent);
368
369 /* Recursively call do_copy() */
370 rc = do_copy(src_dent, dest_dent, blen, vb, recursive,
371 force, interactive);
372 if (rc != EOK)
373 goto exit;
374
375 }
376 } else
377 printf("Unable to open source file %s\n", src);
378
379exit:
380 if (dir)
381 closedir(dir);
382 return rc;
383}
384
385static int copy_file(const char *src, const char *dest,
386 size_t blen, int vb)
387{
388 int fd1, fd2;
389 size_t rbytes, wbytes;
390 errno_t rc;
391 off64_t total;
392 char *buff = NULL;
393 aoff64_t posr = 0, posw = 0;
394 vfs_stat_t st;
395
396 if (vb)
397 printf("Copying %s to %s\n", src, dest);
398
399 rc = vfs_lookup_open(src, WALK_REGULAR, MODE_READ, &fd1);
400 if (rc != EOK) {
401 printf("Unable to open source file %s\n", src);
402 return -1;
403 }
404
405 rc = vfs_lookup_open(dest, WALK_REGULAR | WALK_MAY_CREATE, MODE_WRITE, &fd2);
406 if (rc != EOK) {
407 printf("Unable to open destination file %s\n", dest);
408 vfs_put(fd1);
409 return -1;
410 }
411
412 if (vfs_stat(fd1, &st) != EOK) {
413 printf("Unable to fstat %d\n", fd1);
414 vfs_put(fd1);
415 vfs_put(fd2);
416 return -1;
417 }
418
419 total = st.size;
420 if (vb)
421 printf("%" PRIu64 " bytes to copy\n", total);
422
423 if (NULL == (buff = (char *) malloc(blen))) {
424 printf("Unable to allocate enough memory to read %s\n",
425 src);
426 rc = ENOMEM;
427 goto out;
428 }
429
430 while ((rc = vfs_read(fd1, &posr, buff, blen, &rbytes)) == EOK &&
431 rbytes > 0) {
432 if ((rc = vfs_write(fd2, &posw, buff, rbytes, &wbytes)) != EOK)
433 break;
434 }
435
436 if (rc != EOK) {
437 printf("\nError copying %s: %s\n", src, str_error(rc));
438 return -1;
439 }
440
441out:
442 vfs_put(fd1);
443 vfs_put(fd2);
444 if (buff)
445 free(buff);
446 if (rc != EOK) {
447 return -1;
448 } else {
449 return 0;
450 }
451}
452
453void help_cmd_cp(unsigned int level)
454{
455 static char helpfmt[] =
456 "Usage: %s [options] <source> <dest>\n"
457 "Options:\n"
458 " -h, --help A short option summary\n"
459 " -v, --version Print version information and exit\n"
460 " -V, --verbose Be annoyingly noisy about what's being done\n"
461 " -f, --force Do not complain when <dest> exists (overrides a previous -i)\n"
462 " -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n"
463 " -r, --recursive Copy entire directories\n"
464 " -b, --buffer ## Set the read buffer size to ##\n";
465 if (level == HELP_SHORT) {
466 printf("`%s' copies files and directories\n", cmdname);
467 } else {
468 help_cmd_cp(HELP_SHORT);
469 printf(helpfmt, cmdname);
470 }
471
472 return;
473}
474
475int cmd_cp(char **argv)
476{
477 unsigned int argc, verbose = 0;
478 int buffer = 0, recursive = 0;
479 int force = 0, interactive = 0;
480 int c, opt_ind;
481 errno_t ret;
482
483 con = console_init(stdin, stdout);
484 argc = cli_count_args(argv);
485
486 for (c = 0, optreset = 1, optind = 0, opt_ind = 0; c != -1;) {
487 c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
488 switch (c) {
489 case 'h':
490 help_cmd_cp(1);
491 return CMD_SUCCESS;
492 case 'v':
493 printf("%s\n", CP_VERSION);
494 return CMD_SUCCESS;
495 case 'V':
496 verbose = 1;
497 break;
498 case 'f':
499 interactive = 0;
500 force = 1;
501 break;
502 case 'i':
503 force = 0;
504 interactive = 1;
505 break;
506 case 'r':
507 recursive = 1;
508 break;
509 case 'b':
510 if (-1 == (buffer = strtoint(optarg))) {
511 printf("%s: Invalid buffer specification, "
512 "(should be a number greater than zero)\n",
513 cmdname);
514 console_done(con);
515 return CMD_FAILURE;
516 }
517 if (verbose)
518 printf("Buffer = %d\n", buffer);
519 break;
520 }
521 }
522
523 if (buffer == 0)
524 buffer = CP_DEFAULT_BUFLEN;
525
526 argc -= optind;
527
528 if (argc != 2) {
529 printf("%s: invalid number of arguments. Try %s --help\n",
530 cmdname, cmdname);
531 console_done(con);
532 return CMD_FAILURE;
533 }
534
535 ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
536 recursive, force, interactive);
537
538 console_done(con);
539
540 if (ret == 0)
541 return CMD_SUCCESS;
542 else
543 return CMD_FAILURE;
544}
545
Note: See TracBrowser for help on using the repository browser.