Commit | Line | Data |
---|---|---|
7f9ef9cc KB |
1 | /*- |
2 | * Copyright (c) 1990 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * %sccs.include.redist.c% | |
d1e4967d | 6 | */ |
ae80f4a2 | 7 | |
7f9ef9cc KB |
8 | #ifndef lint |
9 | char copyright[] = | |
10 | "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ | |
11 | All rights reserved.\n"; | |
12 | #endif /* not lint */ | |
d1e4967d | 13 | |
7f9ef9cc | 14 | #ifndef lint |
d6a1ced2 | 15 | static char sccsid[] = "@(#)rm.c 5.2 (Berkeley) %G%"; |
7f9ef9cc | 16 | #endif /* not lint */ |
d1e4967d | 17 | |
7f9ef9cc KB |
18 | #include <sys/types.h> |
19 | #include <sys/stat.h> | |
20 | #include <sys/errno.h> | |
411a0820 KB |
21 | |
22 | #include <err.h> | |
7f9ef9cc | 23 | #include <fts.h> |
7f9ef9cc | 24 | #include <stdio.h> |
7f9ef9cc | 25 | #include <stdlib.h> |
411a0820 KB |
26 | #include <string.h> |
27 | #include <unistd.h> | |
28 | ||
29 | int dflag, fflag, iflag, eval, stdin_ok; | |
ae80f4a2 | 30 | |
411a0820 KB |
31 | int check __P((char *, char *, struct stat *)); |
32 | void checkdot __P((char **)); | |
33 | void rmfile __P((char **)); | |
34 | void rmtree __P((char **)); | |
35 | void usage __P((void)); | |
7f9ef9cc KB |
36 | |
37 | /* | |
38 | * rm -- | |
39 | * This rm is different from historic rm's, but is expected to match | |
40 | * POSIX 1003.2 behavior. The most visible difference is that -f | |
41 | * has two specific effects now, ignore non-existent files and force | |
42 | * file removal. | |
43 | */ | |
411a0820 | 44 | int |
ae80f4a2 | 45 | main(argc, argv) |
8161a03a | 46 | int argc; |
411a0820 | 47 | char *argv[]; |
ae80f4a2 | 48 | { |
8161a03a | 49 | extern int optind; |
7f9ef9cc | 50 | int ch, rflag; |
ae80f4a2 | 51 | |
7f9ef9cc KB |
52 | rflag = 0; |
53 | while ((ch = getopt(argc, argv, "dfiRr")) != EOF) | |
54 | switch(ch) { | |
55 | case 'd': | |
56 | dflag = 1; | |
57 | break; | |
8161a03a | 58 | case 'f': |
7f9ef9cc KB |
59 | fflag = 1; |
60 | iflag = 0; | |
d1e4967d | 61 | break; |
8161a03a | 62 | case 'i': |
7f9ef9cc KB |
63 | fflag = 0; |
64 | iflag = 1; | |
8161a03a KB |
65 | break; |
66 | case 'R': | |
307d7749 | 67 | case 'r': /* compatibility */ |
7f9ef9cc | 68 | rflag = 1; |
8161a03a KB |
69 | break; |
70 | case '?': | |
71 | default: | |
72 | usage(); | |
73 | } | |
7f9ef9cc KB |
74 | argc -= optind; |
75 | argv += optind; | |
ca02483b | 76 | |
7f9ef9cc | 77 | if (argc < 1) |
8161a03a | 78 | usage(); |
ae80f4a2 | 79 | |
7f9ef9cc KB |
80 | checkdot(argv); |
81 | if (!*argv) | |
411a0820 | 82 | exit (eval); |
d1e4967d | 83 | |
7f9ef9cc KB |
84 | stdin_ok = isatty(STDIN_FILENO); |
85 | ||
86 | if (rflag) | |
87 | rmtree(argv); | |
60b32b72 | 88 | else |
7f9ef9cc | 89 | rmfile(argv); |
411a0820 | 90 | exit (eval); |
7f9ef9cc KB |
91 | } |
92 | ||
411a0820 | 93 | void |
7f9ef9cc KB |
94 | rmtree(argv) |
95 | char **argv; | |
96 | { | |
97 | register FTS *fts; | |
98 | register FTSENT *p; | |
99 | register int needstat; | |
7f9ef9cc KB |
100 | |
101 | /* | |
102 | * Remove a file hierarchy. If forcing removal (-f), or interactive | |
103 | * (-i) or can't ask anyway (stdin_ok), don't stat the file. | |
104 | */ | |
105 | needstat = !fflag && !iflag && stdin_ok; | |
106 | ||
107 | /* | |
108 | * If the -i option is specified, the user can skip on the pre-order | |
109 | * visit. The fts_number field flags skipped directories. | |
110 | */ | |
111 | #define SKIPPED 1 | |
112 | ||
113 | if (!(fts = fts_open(argv, | |
114 | needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, | |
411a0820 KB |
115 | (int (*)())NULL))) |
116 | err(1, NULL); | |
7f9ef9cc | 117 | while (p = fts_read(fts)) { |
411a0820 | 118 | switch (p->fts_info) { |
7f9ef9cc | 119 | case FTS_DNR: |
d6a1ced2 KB |
120 | if (!fflag || errno != ENOENT) { |
121 | warn("%s", p->fts_path); | |
122 | eval = 1; | |
123 | } | |
124 | continue; | |
7f9ef9cc | 125 | case FTS_ERR: |
411a0820 | 126 | err(1, "%s", p->fts_path); |
7f9ef9cc | 127 | case FTS_NS: |
411a0820 KB |
128 | /* |
129 | * FTS_NS: assume that if can't stat the file, it | |
130 | * can't be unlinked. | |
131 | */ | |
7f9ef9cc | 132 | if (!needstat) |
d1e4967d | 133 | break; |
411a0820 KB |
134 | if (!fflag || errno != ENOENT) { |
135 | warn("%s", p->fts_path); | |
136 | eval = 1; | |
137 | } | |
7f9ef9cc | 138 | continue; |
7f9ef9cc | 139 | case FTS_D: |
411a0820 | 140 | /* Pre-order: give user chance to skip. */ |
7f9ef9cc | 141 | if (iflag && !check(p->fts_path, p->fts_accpath, |
834beb60 | 142 | p->fts_statp)) { |
7f9ef9cc KB |
143 | (void)fts_set(fts, p, FTS_SKIP); |
144 | p->fts_number = SKIPPED; | |
d1e4967d | 145 | } |
7f9ef9cc | 146 | continue; |
7f9ef9cc | 147 | case FTS_DP: |
411a0820 | 148 | /* Post-order: see if user skipped. */ |
7f9ef9cc KB |
149 | if (p->fts_number == SKIPPED) |
150 | continue; | |
151 | break; | |
d1e4967d | 152 | } |
7f9ef9cc | 153 | if (!fflag && |
834beb60 | 154 | !check(p->fts_path, p->fts_accpath, p->fts_statp)) |
7f9ef9cc KB |
155 | continue; |
156 | ||
157 | /* | |
158 | * If we can't read or search the directory, may still be | |
159 | * able to remove it. Don't print out the un{read,search}able | |
160 | * message unless the remove fails. | |
161 | */ | |
307d7749 | 162 | if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { |
7f9ef9cc KB |
163 | if (!rmdir(p->fts_accpath)) |
164 | continue; | |
165 | if (errno == ENOENT) { | |
166 | if (fflag) | |
167 | continue; | |
168 | } else if (p->fts_info != FTS_DP) | |
411a0820 | 169 | warnx("%s: unable to read", p->fts_path); |
7f9ef9cc | 170 | } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) |
307d7749 | 171 | continue; |
411a0820 KB |
172 | warn("%s", p->fts_path); |
173 | eval = 1; | |
ae80f4a2 | 174 | } |
7f9ef9cc KB |
175 | } |
176 | ||
411a0820 | 177 | void |
7f9ef9cc KB |
178 | rmfile(argv) |
179 | char **argv; | |
180 | { | |
181 | register int df; | |
182 | register char *f; | |
183 | struct stat sb; | |
ae80f4a2 | 184 | |
7f9ef9cc KB |
185 | df = dflag; |
186 | /* | |
187 | * Remove a file. POSIX 1003.2 states that, by default, attempting | |
188 | * to remove a directory is an error, so must always stat the file. | |
189 | */ | |
190 | while (f = *argv++) { | |
191 | /* Assume if can't stat the file, can't unlink it. */ | |
192 | if (lstat(f, &sb)) { | |
411a0820 KB |
193 | if (!fflag || errno != ENOENT) { |
194 | warn("%s", f); | |
195 | eval = 1; | |
196 | } | |
7f9ef9cc | 197 | continue; |
ae80f4a2 | 198 | } |
7f9ef9cc | 199 | if (S_ISDIR(sb.st_mode) && !df) { |
411a0820 KB |
200 | warnx("%s: is a directory", f); |
201 | eval = 1; | |
7f9ef9cc | 202 | continue; |
b2704a87 | 203 | } |
7f9ef9cc KB |
204 | if (!fflag && !check(f, f, &sb)) |
205 | continue; | |
206 | if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) && | |
411a0820 KB |
207 | (!fflag || errno != ENOENT)) { |
208 | warn("%s", f); | |
209 | eval = 1; | |
210 | } | |
ae80f4a2 BJ |
211 | } |
212 | } | |
213 | ||
411a0820 | 214 | int |
7f9ef9cc KB |
215 | check(path, name, sp) |
216 | char *path, *name; | |
217 | struct stat *sp; | |
ae80f4a2 | 218 | { |
7f9ef9cc | 219 | register int first, ch; |
411a0820 | 220 | char modep[15]; |
ae80f4a2 | 221 | |
7f9ef9cc KB |
222 | /* Check -i first. */ |
223 | if (iflag) | |
224 | (void)fprintf(stderr, "remove %s? ", path); | |
225 | else { | |
226 | /* | |
227 | * If it's not a symbolic link and it's unwritable and we're | |
228 | * talking to a terminal, ask. Symbolic links are excluded | |
307d7749 | 229 | * because their permissions are meaningless. |
7f9ef9cc KB |
230 | */ |
231 | if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK)) | |
411a0820 | 232 | return (1); |
7f9ef9cc KB |
233 | strmode(sp->st_mode, modep); |
234 | (void)fprintf(stderr, "override %s%s%s/%s for %s? ", | |
235 | modep + 1, modep[9] == ' ' ? "" : " ", | |
236 | user_from_uid(sp->st_uid, 0), | |
237 | group_from_gid(sp->st_gid, 0), path); | |
238 | } | |
239 | (void)fflush(stderr); | |
240 | ||
241 | first = ch = getchar(); | |
242 | while (ch != '\n' && ch != EOF) | |
243 | ch = getchar(); | |
411a0820 | 244 | return (first == 'y'); |
d1e4967d RC |
245 | } |
246 | ||
7f9ef9cc | 247 | #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) |
411a0820 | 248 | void |
7f9ef9cc KB |
249 | checkdot(argv) |
250 | char **argv; | |
251 | { | |
252 | register char *p, **t, **save; | |
253 | int complained; | |
254 | ||
255 | complained = 0; | |
256 | for (t = argv; *t;) { | |
411a0820 | 257 | if (p = strrchr(*t, '/')) |
7f9ef9cc KB |
258 | ++p; |
259 | else | |
260 | p = *t; | |
261 | if (ISDOT(p)) { | |
262 | if (!complained++) | |
411a0820 KB |
263 | warnx("\".\" and \"..\" may not be removed"); |
264 | eval = 1; | |
7f9ef9cc KB |
265 | for (save = t; t[0] = t[1]; ++t); |
266 | t = save; | |
267 | } else | |
268 | ++t; | |
269 | } | |
270 | } | |
271 | ||
411a0820 | 272 | void |
8161a03a KB |
273 | usage() |
274 | { | |
7f9ef9cc | 275 | (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); |
8161a03a KB |
276 | exit(1); |
277 | } |