summaryrefslogtreecommitdiff
path: root/src/commands/rm.c
blob: 7d72c2d7c18ec575b1440296dbd08cbcf2633227 (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
#include "../command.h"
#include <dirent.h>
#include <string.h>

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

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], "-fr")) {
        printf("\x1b[94mremoving \x1b[97mthe \x1b[91mfrench \x1b[93m(baguette noises)\x1b[0m\n");
    }
#endif

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

    return EXIT_SUCCESS;
}