diff --git a/userspace/programs/rm/main.cpp b/userspace/programs/rm/main.cpp index 8ce474ee..d3e61981 100644 --- a/userspace/programs/rm/main.cpp +++ b/userspace/programs/rm/main.cpp @@ -1,20 +1,50 @@ #include + #include +#include #include -#include #include #include #include -bool delete_recursive(const char* path) +bool prompt_removal(const char* path, struct stat st) +{ + const char* type = "file"; + if (S_ISREG(st.st_mode)) + type = st.st_size ? "regular file" : "regular empty file"; + else if (S_ISDIR(st.st_mode)) + type = "directory"; + else if (S_ISCHR(st.st_mode)) + type = "character special file"; + else if (S_ISBLK(st.st_mode)) + type = "block special file"; + else if (S_ISLNK(st.st_mode)) + type = "symbolic link"; + else if (S_ISFIFO(st.st_mode)) + type = "fifo"; + + fprintf(stderr, "remove %s '%s'? ", type, path); + + char buffer[128]; + if (fgets(buffer, sizeof(buffer), stdin) == nullptr) + return false; + return buffer[0] == 'Y' || buffer[0] == 'y'; +} + +bool delete_recursive(const char* path, bool force, bool interactive) { struct stat st; if (stat(path, &st) == -1) { + if (force && errno == ENOENT) + return true; perror(path); return false; } + if (interactive && !prompt_removal(path, st)) + return true; + bool ret = true; if (S_ISDIR(st.st_mode)) @@ -32,11 +62,13 @@ bool delete_recursive(const char* path) if (dirent->d_type == DT_DIR) { - if (!delete_recursive(dirent_path.data())) + if (!delete_recursive(dirent_path.data(), force, interactive)) ret = false; } else { + if (interactive && !prompt_removal(dirent_path.data(), st)) + continue; if (unlink(dirent_path.data()) == -1) { perror(dirent_path.data()); @@ -57,75 +89,97 @@ bool delete_recursive(const char* path) return ret; } -void usage(const char* argv0, int ret) -{ - FILE* out = (ret == 0) ? stdout : stderr; - fprintf(out, "usage: %s [OPTIONS]... FILE...\n", argv0); - fprintf(out, " remove each FILE\n"); - fprintf(out, "OPTIONS:\n"); - fprintf(out, " -r remove directories and their contents recursively\n"); - fprintf(out, " -h, --help show this message and exit\n"); - exit(ret); -} - int main(int argc, char** argv) { + bool force = false; + bool interactive = false; bool recursive = false; - int i = 1; - for (; i < argc; i++) + for (;;) { - if (argv[i][0] != '-') + static option long_options[] { + { "recursive", no_argument, nullptr, 'r' }, + { "interactive", no_argument, nullptr, 'i' }, + { "force", no_argument, nullptr, 'f' }, + { "help", no_argument, nullptr, 'h' }, + }; + + int ch = getopt_long(argc, argv, "rRifh", long_options, nullptr); + if (ch == -1) break; - if (strcmp(argv[i], "-r") == 0) - recursive = true; - else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) - usage(argv[0], 0); - else + switch (ch) { - fprintf(stderr, "unrecognized argument %s. use --help for more information\n", argv[i]); - return 1; + case 'h': + fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]); + fprintf(stderr, " remove each FILE\n"); + fprintf(stderr, "OPTIONS:\n"); + fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n"); + fprintf(stderr, " -i, --interactive prompt removal for all files\n"); + fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n"); + fprintf(stderr, " -h, --help show this message and exit\n"); + return 0; + case 'r': case 'R': + recursive = true; + break; + case 'f': + force = true; + interactive = false; + break; + case 'i': + force = false; + interactive = true; + break; + case '?': + fprintf(stderr, "invalid option %c\n", optopt); + fprintf(stderr, "see '%s --help' for usage\n", argv[0]); + return 1; } } - if (i >= argc) + if (optind >= argc && !force) { fprintf(stderr, "missing operand. use --help for more information\n"); return 1; } int ret = 0; - for (; i < argc; i++) + for (int i = optind; i < argc; i++) { if (recursive) { - if (!delete_recursive(argv[i])) + if (!delete_recursive(argv[i], force, interactive)) ret = 1; + continue; } - else + + struct stat st; + if (stat(argv[i], &st) == -1) { - struct stat st; - if (stat(argv[i], &st) == -1) - { - perror(argv[i]); - ret = 1; + if (force && errno == ENOENT) continue; - } + perror(argv[i]); + ret = 1; + continue; + } - if (S_ISDIR(st.st_mode)) - { - fprintf(stderr, "%s: %s\n", argv[i], strerror(EISDIR)); - ret = 1; - continue; - } + if (interactive && !prompt_removal(argv[i], st)) + continue; - if (unlink(argv[i]) == -1) - { - perror(argv[i]); - ret = 1; - } + if (S_ISDIR(st.st_mode)) + { + errno = EISDIR; + perror(argv[i]); + ret = 1; + continue; + } + + if (unlink(argv[i]) == -1) + { + perror(argv[i]); + ret = 1; } } + return ret; }