1/* See LICENSE file for copyright and license details. */
2
3#include <fcntl.h>
4#include <string.h>
5
6#include "fs.h"
7#include "util.h"
8
9static void
10usage(void)
11{
12 eprintf("usage: %s [-f] [-iRr] file ...\n", argv0);
13}
14
15static int
16forbidden(char *path, struct stat *root)
17{
18 char *s, *t;
19 size_t n;
20 struct stat st;
21 static int w1, w2;
22
23 n = strlen(path);
24 for (t = path + n; t > path && t[-1] == '/'; --t)
25 ;
26 for (s = t; s > path && s[-1] != '/'; --s)
27 ;
28 n = t - s;
29 if ((n == 1 && *s == '.') || (n == 2 && s[0] == '.' && s[1] == '.')) {
30 if (!w1)
31 weprintf("\".\" and \"..\" may not be removed\n");
32 w1 = 1;
33 return 1;
34 }
35
36 if (stat(path, &st) < 0)
37 return 0;
38 if (st.st_dev == root->st_dev && st.st_ino == root->st_ino) {
39 if (!w2)
40 weprintf("\"/\" may not be removed\n");
41 w2 = 1;
42 return 1;
43 }
44
45 return 0;
46}
47
48// ?man rm: remove files
49// ?man arguments: file ...
50// ?man remove files and directory hierarchies
51int
52main(int argc, char *argv[])
53{
54 struct stat st;
55 struct recursor r = {.fn = rm, .maxdepth = 1, .follow = 'P'};
56
57 ARGBEGIN
58 {
59 // ?man -f: ignore nonexistent files and never prompt
60 case 'f':
61 r.flags |= SILENT | IGNORE;
62 break;
63 // ?man -i: prompt before every removal
64 case 'i':
65 r.flags |= CONFIRM;
66 break;
67 // ?man -R: remove directories and their contents recursively
68 case 'R':
69 // ?man -r: remove directories and their contents recursively
70 case 'r':
71 r.maxdepth = 0;
72 break;
73 default:
74 usage();
75 }
76 ARGEND
77
78 if (!argc) {
79 if (!(r.flags & IGNORE))
80 usage();
81 else
82 return 0;
83 }
84
85 if (stat("/", &st) < 0)
86 eprintf("stat root:");
87 for (; *argv; argc--, argv++) {
88 if (forbidden(*argv, &st)) {
89 rm_status = 1;
90 continue;
91 }
92 recurse(AT_FDCWD, *argv, NULL, &r);
93 }
94
95 return rm_status || recurse_status;
96}