Changes in uspace/app/bdsh/cmds/modules/cp/cp.c [6348376:e367f5b8] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/app/bdsh/cmds/modules/cp/cp.c
r6348376 re367f5b8 30 30 #include <stdlib.h> 31 31 #include <unistd.h> 32 #include <io/console.h> 33 #include <io/keycode.h> 32 34 #include <getopt.h> 33 35 #include <str.h> 34 36 #include <fcntl.h> 37 #include <sys/stat.h> 38 #include <dirent.h> 35 39 #include "config.h" 36 40 #include "util.h" … … 44 48 45 49 static const char *cmdname = "cp"; 50 static console_ctrl_t *con; 46 51 47 52 static struct option const long_options[] = { 48 53 { "buffer", required_argument, 0, 'b' }, 49 54 { "force", no_argument, 0, 'f' }, 55 { "interactive", no_argument, 0, 'i'}, 50 56 { "recursive", no_argument, 0, 'r' }, 51 57 { "help", no_argument, 0, 'h' }, … … 55 61 }; 56 62 63 typedef enum { 64 TYPE_NONE, 65 TYPE_FILE, 66 TYPE_DIR 67 } dentry_type_t; 68 69 static int64_t 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 */ 80 static dentry_type_t get_type(const char *path) 81 { 82 struct stat s; 83 84 int r = stat(path, &s); 85 86 if (r) 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 57 96 static int strtoint(const char *s1) 58 97 { … … 66 105 67 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 */ 117 static 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 */ 136 static 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 145 static 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 kbd_event_t ev; 155 console_flush(con); 156 console_get_kbd_event(con, &ev); 157 if ((ev.type != KEY_PRESS) 158 || (ev.mods & (KM_CTRL | KM_ALT)) != 0) { 159 continue; 160 } 161 162 switch(ev.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 178 static int64_t do_copy(const char *src, const char *dest, 179 size_t blen, int vb, int recursive, int force, int interactive) 180 { 181 int r = -1; 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 goto exit; 221 } 222 } 223 224 if (dest_type == TYPE_DIR) { 225 printf("Cannot overwrite existing directory %s\n", 226 dest_path); 227 goto exit; 228 } else if (dest_type == TYPE_FILE) { 229 /* e.g. cp file_name existing_file */ 230 231 /* dest already exists, 232 * if force is set we will try to remove it. 233 * if interactive is set user input is required. 234 */ 235 if (force && !interactive) { 236 if (unlink(dest_path)) { 237 printf("Unable to remove %s\n", 238 dest_path); 239 goto exit; 240 } 241 } else if (!force && interactive) { 242 bool overwrite = get_user_decision(false, 243 "File already exists: %s. Overwrite? [y/N]: ", 244 dest_path); 245 if (overwrite) { 246 printf("Overwriting file: %s\n", dest_path); 247 if (unlink(dest_path)) { 248 printf("Unable to remove %s\n", dest_path); 249 goto exit; 250 } 251 } else { 252 printf("Not overwriting file: %s\n", dest_path); 253 r = 0; 254 goto exit; 255 } 256 } else { 257 printf("File already exists: %s\n", dest_path); 258 goto exit; 259 } 260 } 261 262 /* call copy_file and exit */ 263 r = (copy_file(src, dest_path, blen, vb) < 0); 264 265 } else if (src_type == TYPE_DIR) { 266 /* e.g. cp -r /x/srcdir /y/destdir/ */ 267 268 if (!recursive) { 269 printf("Cannot copy the %s directory without the " 270 "-r option\n", src); 271 goto exit; 272 } else if (dest_type == TYPE_FILE) { 273 printf("Cannot overwrite a file with a directory\n"); 274 goto exit; 275 } 276 277 char *src_dirname; 278 279 /* Initialize src_path with the content of src */ 280 str_cpy(src_path, src_len + 1, src); 281 str_rtrim(src_path, '/'); 282 283 src_dirname = get_last_path_component(src_path); 284 285 str_cpy(dest_path, PATH_MAX, dest); 286 287 switch (dest_type) { 288 case TYPE_DIR: 289 if (str_cmp(src_dirname, "..") && 290 str_cmp(src_dirname, ".")) { 291 /* The last component of src_path is 292 * not '.' or '..' 293 */ 294 merge_paths(dest_path, PATH_MAX, src_dirname); 295 296 if (mkdir(dest_path, 0) == -1) { 297 printf("Unable to create " 298 "dest directory %s\n", dest_path); 299 goto exit; 300 } 301 } 302 break; 303 default: 304 case TYPE_NONE: 305 /* dest does not exists, this means the user wants 306 * to specify the name of the destination directory 307 * 308 * e.g. cp -r /src /data/new_dir_src 309 */ 310 if (mkdir(dest_path, 0)) { 311 printf("Unable to create " 312 "dest directory %s\n", dest_path); 313 goto exit; 314 } 315 break; 316 } 317 318 dir = opendir(src); 319 if (!dir) { 320 /* Something strange is happening... */ 321 printf("Unable to open src %s directory\n", src); 322 goto exit; 323 } 324 325 /* Copy every single directory entry of src into the 326 * destination directory. 327 */ 328 while ((dp = readdir(dir))) { 329 struct stat src_s; 330 struct stat dest_s; 331 332 char src_dent[PATH_MAX]; 333 char dest_dent[PATH_MAX]; 334 335 str_cpy(src_dent, PATH_MAX, src); 336 merge_paths(src_dent, PATH_MAX, dp->d_name); 337 338 str_cpy(dest_dent, PATH_MAX, dest_path); 339 merge_paths(dest_dent, PATH_MAX, dp->d_name); 340 341 /* Check if we are copying a directory into itself */ 342 stat(src_dent, &src_s); 343 stat(dest_path, &dest_s); 344 345 if (dest_s.index == src_s.index && 346 dest_s.fs_handle == src_s.fs_handle) { 347 printf("Cannot copy a directory " 348 "into itself\n"); 349 goto exit; 350 } 351 352 if (vb) 353 printf("copy %s %s\n", src_dent, dest_dent); 354 355 /* Recursively call do_copy() */ 356 r = do_copy(src_dent, dest_dent, blen, vb, recursive, 357 force, interactive); 358 if (r) 359 goto exit; 360 361 } 362 } else 363 printf("Unable to open source file %s\n", src); 364 365 exit: 366 if (dir) 367 closedir(dir); 368 return r; 68 369 } 69 370 … … 127 428 static char helpfmt[] = 128 429 "Usage: %s [options] <source> <dest>\n" 129 "Options: (* indicates not yet implemented)\n"430 "Options:\n" 130 431 " -h, --help A short option summary\n" 131 432 " -v, --version Print version information and exit\n" 132 " *-V, --verbose Be annoyingly noisy about what's being done\n"133 " * -f, --force Do not complain when <dest> exists\n"134 " * -r, --recursive Copy entire directories\n"135 " - b, --buffer ## Set the read buffer size to ##\n"136 " Currently, %s is under development, some options may not work.\n";433 " -V, --verbose Be annoyingly noisy about what's being done\n" 434 " -f, --force Do not complain when <dest> exists (overrides a previous -i)\n" 435 " -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n" 436 " -r, --recursive Copy entire directories\n" 437 " -b, --buffer ## Set the read buffer size to ##\n"; 137 438 if (level == HELP_SHORT) { 138 439 printf("`%s' copies files and directories\n", cmdname); 139 440 } else { 140 441 help_cmd_cp(HELP_SHORT); 141 printf(helpfmt, cmdname , cmdname);442 printf(helpfmt, cmdname); 142 443 } 143 444 … … 148 449 { 149 450 unsigned int argc, verbose = 0; 150 int buffer = 0; 451 int buffer = 0, recursive = 0; 452 int force = 0, interactive = 0; 151 453 int c, opt_ind; 152 454 int64_t ret; 153 455 456 con = console_init(stdin, stdout); 154 457 argc = cli_count_args(argv); 155 458 156 459 for (c = 0, optind = 0, opt_ind = 0; c != -1;) { 157 c = getopt_long(argc, argv, "hvVf rb:", long_options, &opt_ind);460 c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind); 158 461 switch (c) { 159 462 case 'h': … … 167 470 break; 168 471 case 'f': 472 interactive = 0; 473 force = 1; 474 break; 475 case 'i': 476 force = 0; 477 interactive = 1; 169 478 break; 170 479 case 'r': 480 recursive = 1; 171 481 break; 172 482 case 'b': … … 175 485 "(should be a number greater than zero)\n", 176 486 cmdname); 487 console_done(con); 177 488 return CMD_FAILURE; 178 489 } … … 191 502 printf("%s: invalid number of arguments. Try %s --help\n", 192 503 cmdname, cmdname); 504 console_done(con); 193 505 return CMD_FAILURE; 194 506 } 195 507 196 ret = copy_file(argv[optind], argv[optind + 1], buffer, verbose);197 198 if (verbose) 199 printf("%" PRId64 " bytes copied\n", ret);200 201 if (ret >= 0)508 ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose, 509 recursive, force, interactive); 510 511 console_done(con); 512 513 if (ret == 0) 202 514 return CMD_SUCCESS; 203 515 else
Note:
See TracChangeset
for help on using the changeset viewer.