Serenity Operating System
at master 101 lines 3.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/LexicalPath.h> 9#include <LibCore/ArgsParser.h> 10#include <LibCore/DeprecatedFile.h> 11#include <LibCore/System.h> 12#include <LibMain/Main.h> 13#include <stdio.h> 14#include <string.h> 15#include <sys/stat.h> 16#include <unistd.h> 17 18ErrorOr<int> serenity_main(Main::Arguments arguments) 19{ 20 TRY(Core::System::pledge("stdio rpath wpath cpath fattr")); 21 22 bool force = false; 23 bool no_clobber = false; 24 bool verbose = false; 25 26 Vector<DeprecatedString> paths; 27 28 Core::ArgsParser args_parser; 29 args_parser.add_option(force, "Force", "force", 'f'); 30 args_parser.add_option(no_clobber, "Do not overwrite existing files", "no-clobber", 'n'); 31 args_parser.add_option(verbose, "Verbose", "verbose", 'v'); 32 args_parser.add_positional_argument(paths, "Paths to files being moved followed by target location", "paths"); 33 args_parser.parse(arguments); 34 35 if (paths.size() < 2) { 36 args_parser.print_usage(stderr, arguments.strings[0]); 37 return 1; 38 } 39 40 if (force && no_clobber) { 41 warnln("-f (--force) overrides -n (--no-clobber)"); 42 no_clobber = false; 43 } 44 45 auto original_new_path = paths.take_last(); 46 47 struct stat st; 48 49 int rc = lstat(original_new_path.characters(), &st); 50 if (rc != 0 && errno != ENOENT) { 51 perror("lstat"); 52 return 1; 53 } 54 55 if (paths.size() > 1 && !S_ISDIR(st.st_mode)) { 56 warnln("Target is not a directory: {}", original_new_path); 57 return 1; 58 } 59 60 auto my_umask = umask(0); 61 umask(my_umask); 62 63 for (auto& old_path : paths) { 64 DeprecatedString combined_new_path; 65 auto new_path = original_new_path; 66 if (S_ISDIR(st.st_mode)) { 67 auto old_basename = LexicalPath::basename(old_path); 68 combined_new_path = DeprecatedString::formatted("{}/{}", original_new_path, old_basename); 69 new_path = combined_new_path.characters(); 70 } 71 72 if (no_clobber && Core::DeprecatedFile::exists(new_path)) 73 continue; 74 75 rc = rename(old_path.characters(), new_path.characters()); 76 if (rc < 0) { 77 if (errno == EXDEV) { 78 auto result = Core::DeprecatedFile::copy_file_or_directory( 79 new_path, old_path, 80 Core::DeprecatedFile::RecursionMode::Allowed, 81 Core::DeprecatedFile::LinkMode::Disallowed, 82 Core::DeprecatedFile::AddDuplicateFileMarker::No); 83 84 if (result.is_error()) { 85 warnln("mv: could not move '{}': {}", old_path, static_cast<Error const&>(result.error())); 86 return 1; 87 } 88 rc = unlink(old_path.characters()); 89 if (rc < 0) 90 warnln("mv: unlink '{}': {}", old_path, strerror(errno)); 91 } else { 92 warnln("mv: cannot move '{}' : {}", old_path, strerror(errno)); 93 } 94 } 95 96 if (verbose && rc == 0) 97 outln("renamed '{}' -> '{}'", old_path, new_path); 98 } 99 100 return 0; 101}