rm: add -f and -i options
rm *should* now be posix compatible
This commit is contained in:
parent
7fdfad4088
commit
2efd6f92b2
|
|
@ -1,20 +1,50 @@
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
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;
|
struct stat st;
|
||||||
if (stat(path, &st) == -1)
|
if (stat(path, &st) == -1)
|
||||||
{
|
{
|
||||||
|
if (force && errno == ENOENT)
|
||||||
|
return true;
|
||||||
perror(path);
|
perror(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (interactive && !prompt_removal(path, st))
|
||||||
|
return true;
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
|
|
@ -32,11 +62,13 @@ bool delete_recursive(const char* path)
|
||||||
|
|
||||||
if (dirent->d_type == DT_DIR)
|
if (dirent->d_type == DT_DIR)
|
||||||
{
|
{
|
||||||
if (!delete_recursive(dirent_path.data()))
|
if (!delete_recursive(dirent_path.data(), force, interactive))
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (interactive && !prompt_removal(dirent_path.data(), st))
|
||||||
|
continue;
|
||||||
if (unlink(dirent_path.data()) == -1)
|
if (unlink(dirent_path.data()) == -1)
|
||||||
{
|
{
|
||||||
perror(dirent_path.data());
|
perror(dirent_path.data());
|
||||||
|
|
@ -57,75 +89,97 @@ bool delete_recursive(const char* path)
|
||||||
return ret;
|
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)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
bool force = false;
|
||||||
|
bool interactive = false;
|
||||||
bool recursive = false;
|
bool recursive = false;
|
||||||
|
|
||||||
int i = 1;
|
for (;;)
|
||||||
for (; i < argc; i++)
|
|
||||||
{
|
{
|
||||||
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;
|
break;
|
||||||
|
|
||||||
if (strcmp(argv[i], "-r") == 0)
|
switch (ch)
|
||||||
recursive = true;
|
|
||||||
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
|
|
||||||
usage(argv[0], 0);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "unrecognized argument %s. use --help for more information\n", argv[i]);
|
case 'h':
|
||||||
return 1;
|
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");
|
fprintf(stderr, "missing operand. use --help for more information\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
for (; i < argc; i++)
|
for (int i = optind; i < argc; i++)
|
||||||
{
|
{
|
||||||
if (recursive)
|
if (recursive)
|
||||||
{
|
{
|
||||||
if (!delete_recursive(argv[i]))
|
if (!delete_recursive(argv[i], force, interactive))
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(argv[i], &st) == -1)
|
||||||
{
|
{
|
||||||
struct stat st;
|
if (force && errno == ENOENT)
|
||||||
if (stat(argv[i], &st) == -1)
|
|
||||||
{
|
|
||||||
perror(argv[i]);
|
|
||||||
ret = 1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
perror(argv[i]);
|
||||||
|
ret = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode))
|
if (interactive && !prompt_removal(argv[i], st))
|
||||||
{
|
continue;
|
||||||
fprintf(stderr, "%s: %s\n", argv[i], strerror(EISDIR));
|
|
||||||
ret = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlink(argv[i]) == -1)
|
if (S_ISDIR(st.st_mode))
|
||||||
{
|
{
|
||||||
perror(argv[i]);
|
errno = EISDIR;
|
||||||
ret = 1;
|
perror(argv[i]);
|
||||||
}
|
ret = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink(argv[i]) == -1)
|
||||||
|
{
|
||||||
|
perror(argv[i]);
|
||||||
|
ret = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue