-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdeldir.c
338 lines (289 loc) · 9.31 KB
/
deldir.c
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*
* deldir.c - Recursively delete a folder, even with a huge number of inodes
* Copyright (C) 2013 Ayron Jungren
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _XOPEN_SOURCE 700 /* for lstat */
#include <ctype.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define BUFFER_SIZE 8
#define USAGE_HEADER "Usage: %s [-h] [-q] [-s] [-v] [-y]\n"
#define USAGE_HELP "Try `%s -h' for more information.\n"
#define VERSION "0.2-git"
/* C99 and up supports the `restrict' keyword
* (see https://en.wikipedia.org/wiki/Restrict) */
#if __STDC_VERSION__ >= 199901L
#define C99_TEXT "with C99 support"
#define _inline inline
#define _restrict restrict
#else
#define C99_TEXT "without C99 support"
#define _inline
#define _restrict
#endif
/* GCC and GCC-compatible compilers support __attribute__ so it needs to be
* dummied out if the compiler is not GCC-compatible */
#ifndef __GNUC__
#define __attribute__(x)
#endif
/* Function prototypes */
bool confirm_deldir(const char * _restrict path);
bool deldir(const char * _restrict path, const char * _restrict starting_path, bool suppress_warning);
_inline int do_rmdir(const char * _restrict path);
_inline int do_unlink(const char * _restrict path);
char *get_current_path(void);
char *parse_arguments(int argc, char **argv);
void print_current_path(void);
void print_help(char *name) __attribute__((noreturn));
void print_usage(char *name) __attribute__((noreturn));
void print_version(void) __attribute__((noreturn));
int main(int argc, char **argv);
/* Global variables */
bool assume_yes = false, quiet = false, simulate = false;
bool confirm_deldir(const char * _restrict path) {
char * _restrict buffer;
int index;
if(assume_yes) {
fprintf(stderr, "WARNING: *ALL* files in `%s' will be deleted!\n"
"Continuing in five seconds... (Press Ctrl-C to cancel)\n", path);
for(index = 0; index < 5; index++) {
fprintf(stderr, ".\a");
fflush(stderr);
sleep(1);
}
fprintf(stderr, "\n");
return true;
}
if(simulate)
return true;
buffer = malloc(BUFFER_SIZE);
if(buffer == NULL) {
perror("Could not allocate buffer");
return false;
}
fprintf(stderr, "WARNING: *ALL* files in `%s' will be deleted! Continue?\n"
"Type \"yes\" and press ENTER to confirm, or anything else to cancel.\n> ", path);
fflush(stdin);
if(fgets(buffer, BUFFER_SIZE, stdin) == NULL) {
perror("Could not read from stdin");
free(buffer);
return false;
}
if(strcmp(buffer, "yes\n") == 0) {
free(buffer);
return true;
}
else {
puts("Cancelling!");
free(buffer);
return false;
}
}
bool deldir(const char * _restrict path, const char * _restrict starting_path, bool suppress_warning) {
char * _restrict full_path = NULL;
DIR * _restrict handle;
struct dirent * _restrict entry;
struct stat * _restrict file = NULL;
if(chdir(path) != 0) {
perror("Could not change directory");
goto error;
}
file = malloc(sizeof(struct stat));
if(file == NULL) {
perror("Could not allocate memory");
goto error;
}
full_path = get_current_path();
if(full_path == NULL)
goto error;
if(!suppress_warning && !confirm_deldir(full_path))
goto error;
handle = opendir(full_path);
if(handle == NULL) {
perror("Could not open directory");
goto error;
}
while((entry = readdir(handle))) {
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if(lstat(entry->d_name, file) != 0) {
perror("Could not stat file");
goto error;
}
if(S_ISDIR(file->st_mode)) {
if(!quiet)
printf("Entering folder `%s'\n", entry->d_name);
if(!deldir(entry->d_name, full_path, true)) {
/* There isn't an error to print with perror as the child deldir
* already called it. */
goto error;
}
if(do_rmdir(entry->d_name) != 0) {
perror("Could not delete directory");
goto error;
}
}
else {
if(!quiet)
printf("Deleting `%s'...", entry->d_name);
if(do_unlink(entry->d_name) != 0) {
if(!quiet)
puts(" FAILED");
perror("Could not unlink file");
goto error;
}
else if(!quiet)
puts(" OK");
}
}
closedir(handle);
if(starting_path == NULL) {
if(do_rmdir(full_path) != 0) {
perror("Could not delete directory");
goto error;
}
}
else if(chdir(starting_path) != 0) {
perror("Could not change directory");
goto error;
}
free(file);
free(full_path);
return true;
error:
free(file);
free(full_path);
return false;
}
_inline int do_rmdir(const char * _restrict path) {
if(simulate) {
printf("rmdir(\"%s\") in ", path);
print_current_path();
return 0;
}
else
return rmdir(path);
}
_inline int do_unlink(const char * _restrict path) {
if(simulate) {
printf("unlink(\"%s\") in ", path);
print_current_path();
return 0;
}
else
return unlink(path);
}
char *get_current_path(void) {
char *path;
path = getcwd(NULL, 0);
if(path == NULL) {
perror("Could not get current path");
return NULL;
}
return path;
}
char *parse_arguments(int argc, char **argv) {
int option;
while((option = getopt(argc, argv, "hqsvy")) != -1) {
switch(option) {
case 'h':
print_help(argv[0]);
break;
case 'q':
quiet = true;
break;
case 's':
quiet = true;
simulate = true;
break;
case 'v':
print_version();
break;
case 'y':
assume_yes = true;
break;
default:
fprintf(stderr, USAGE_HELP, argv[0]);
return NULL;
}
}
if(optind == argc-1)
return argv[optind];
else
print_usage(argv[0]);
#ifndef __GNUC__
/* Make the compiler happy (not needed on GCC-compatible compilers as
* the noreturn attribute on print_usage will prevent the warning) */
return NULL;
#endif
}
void print_current_path(void) {
char *path = get_current_path();
if(path == NULL)
return;
puts(path);
free(path);
}
void print_help(char *name) {
fprintf(stderr, USAGE_HEADER, name);
/* These strings are split up because C90 only requires compilers to support
* 509 character long strings */
fputs("Recursively delete a folder, even with a huge number of inodes\n"
"Should work even when `rm' and other UNIX tools do not.\n"
"\n"
" -h print this help and exit\n"
" -q do not print files and folders as they are deleted\n"
" -s simulate deleting files and folders, do not actually delete\n"
" -v print the version of deldir and exit\n"
" -y assume the confirmation to delete is answered with `yes'\n"
, stderr);
fputs("deldir home page: <http://0x3b.com/projects/deldir>\n"
"Report deldir bugs at <http://0x3b.com/projects/deldir/issues>\n"
"deldir is licensed under the GNU General Public License (GPL) version 3 ONLY.\n"
, stderr);
fprintf(stderr, "Run `%s -v' for more information about licensing.\n", name);
exit(EXIT_FAILURE);
}
void print_usage(char *name) {
fprintf(stderr, USAGE_HEADER, name);
fprintf(stderr, USAGE_HELP, name);
exit(EXIT_FAILURE);
}
void print_version(void) {
fputs("deldir " VERSION "\n"
"Built " C99_TEXT "\n"
"Copyright (C) 2013 Ayron Jungren\n"
"License GPLv3: GNU GPL version 3 ONLY <https://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
, stderr);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv) {
char *path = parse_arguments(argc, argv);
if(path == NULL)
return EXIT_FAILURE;
if(deldir(path, NULL, false))
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}