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