summaryrefslogtreecommitdiff
path: root/src/commands/rm.c
blob: b85f91af1c79a0a53c67d3b403eb5fdd99773622 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include "../command.h"
#include <dirent.h>
#include <string.h>

static struct {
    bool force;
    bool prompt;
    bool verbose;
    bool recurse;
} flags;

#ifdef FRENCH
    static bool get_frenched;
#endif

static void help(void) {
    printf("Usage: rm [-irfv] FILE...\n\n");
    printf("Remove (unlink) FILESs\n\n");
    printf("\t-i\tAlways prompt before removing\n");
    printf("\t-f\tForce, never prompt\n");
    printf("\t-v\tVerbose\n");
    printf("\t-R,-r\tRecurse\n");
}

static int short_arg(char c, char* next) {
    UNUSED(next);
    switch (c) {
        case 'i':
            flags.prompt = true;
            break;
        case 'f':
            flags.force = true;
            break;
        case 'v':
            flags.verbose = true;
            break;
        case 'R':
        case 'r':
            flags.recurse = true;
            break;
        default:
            return ARG_INVALID;
    }
    return ARG_UNUSED;
}

static void rm_file(char* path);

static bool rm_dir() {
    DIR* d = opendir(get_path_buffer());
    if (d == NULL) {
        error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno));
        return false;
    }

    struct dirent* file;
    while ((file = readdir(d)) != NULL) {
        if (is_dot_dir(file->d_name)) continue;
        rm_file(file->d_name);
    }

    closedir(d);
    return true;
}

static void rm_file(char* path) {
    int save = push_path_buffer(path);

    struct stat s;
    if (lstat(get_path_buffer(), &s) < 0) {
        pop_path_buffer(save);
        error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno));
        return;
    }

    if (S_ISDIR(s.st_mode)) {
        if (!flags.force) {
            error_s("cannot delete '%s': Is a directory\n", get_path_buffer());
            pop_path_buffer(save);
            return;
        }
        if (flags.recurse && !rm_dir()) {
            pop_path_buffer(save);
            return;
        }
    }

    if (flags.prompt) {
        fprintf(stderr, "delete '%s'? ", get_path_buffer());
        fflush(stderr);
        char c = getchar();
        if (c != 'y' && c != 'Y') {
            fprintf(stderr, "Skipping...\n");
            pop_path_buffer(save);
            return;
        }
    }

    if (remove(get_path_buffer()) < 0) {
        error_s("failed to delete '%s': %s\n", get_path_buffer(), strerror(errno));
    } else if (flags.verbose) {
        output("deleted '%s'\n", get_path_buffer());
    }

    pop_path_buffer(save);
}

COMMAND(rm) {
    if (argc < 1) {
        global_help(help);
        return EXIT_SUCCESS;
    }

    flags.prompt = false;
    flags.force = false;
    flags.verbose = false;
    flags.recurse = false;

    int start = parse_args(argc, argv, help, short_arg, NULL);

#ifdef FRENCH
    if (streql(argv[0], "-rf")) {
        printf("\x1b[94mremoving \x1b[97mthe \x1b[91mfrench \x1b[93m(beguette noises)\x1b[0m\n");
    }
#endif

    for (int i = start; i < argc; i++) {
        rm_file(argv[i]);
    }

    return EXIT_SUCCESS;
}