1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
26 * rm [-fir] [file ...]
29 static const char usage
[] =
30 "[-?\n@(#)$Id: rm (AT&T Research) 2009-06-18 $\n]"
32 "[+NAME?rm - remove files]"
33 "[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
34 " does not remove directories. If a file is unwritable, the"
35 " standard input is a terminal, and the \b--force\b option is not"
36 " given, \brm\b prompts the user for whether to remove the file."
37 " An affirmative response (\by\b or \bY\b) removes the file, a quit"
38 " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
39 " all other responses skip the current file.]"
41 "[c|F:clear|clobber?Clear the contents of each file before removing by"
42 " writing a 0 filled buffer the same size as the file, executing"
43 " \bfsync\b(2) and closing before attempting to remove. Implemented"
44 " only on systems that support \bfsync\b(2).]"
45 "[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than"
46 " \brmdir\b(2), and don't require that they be empty before removal."
47 " The caller requires sufficient privilege, not to mention a strong"
48 " constitution, to use this option. Even though the directory must"
49 " not be empty, \brm\b still attempts to empty it before removal.]"
50 "[f:force?Ignore nonexistent files and never prompt the user.]"
51 "[i:interactive|prompt?Prompt whether to remove each file."
52 " An affirmative response (\by\b or \bY\b) removes the file, a quit"
53 " response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
54 " all other responses skip the current file.]"
55 "[r|R:recursive?Remove the contents of directories recursively.]"
56 "[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then"
57 " the owner read, write and execute modes are enabled (if not already"
58 " enabled) for each directory before attempting to remove directory"
60 "[v:verbose?Print the name of each file before removing it.]"
66 "[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]"
76 #define beenhere(f) (((f)->fts_number>>1)==(f)->fts_statp->st_nlink)
77 #define isempty(f) (!((f)->fts_number&RM_ENTRY))
78 #define nonempty(f) ((f)->fts_parent->fts_number|=RM_ENTRY)
79 #define pathchunk(n) roundof(n,1024)
80 #define retry(f) ((f)->fts_number=((f)->fts_statp->st_nlink<<1))
82 typedef struct State_s
/* program state */
84 void* context
; /* builtin context */
85 int clobber
; /* clear out file data first */
86 int directory
; /* remove(dir) not rmdir(dir) */
87 int force
; /* force actions */
88 int fs3d
; /* 3d enabled */
89 int interactive
; /* prompt for approval */
90 int recursive
; /* remove subtrees too */
91 int terminal
; /* attached to terminal */
92 int uid
; /* caller uid */
93 int unconditional
; /* enable dir rwx on preorder */
94 int verbose
; /* display each file */
96 char buf
[SF_BUFSIZE
];/* clobber buffer */
101 * remove a single file
105 rm(State_t
* state
, register FTSENT
* ent
)
112 if (ent
->fts_info
== FTS_NS
|| ent
->fts_info
== FTS_ERR
|| ent
->fts_info
== FTS_SLNONE
)
115 error(2, "%s: not found", ent
->fts_path
);
117 else if (state
->fs3d
&& iview(ent
->fts_statp
))
118 fts_set(NiL
, ent
, FTS_SKIP
);
119 else switch (ent
->fts_info
)
123 if (state
->unconditional
)
125 if (!chmod(ent
->fts_name
, (ent
->fts_statp
->st_mode
& S_IPERM
)|S_IRWXU
))
127 fts_set(NiL
, ent
, FTS_AGAIN
);
132 else if (!state
->force
)
133 error(2, "%s: cannot %s directory", ent
->fts_path
, (ent
->fts_info
& FTS_NR
) ? "read" : "search");
136 fts_set(NiL
, ent
, FTS_SKIP
);
141 path
= ent
->fts_name
;
142 if (path
[0] == '.' && (!path
[1] || path
[1] == '.' && !path
[2]) && (ent
->fts_level
> 0 || path
[1]))
144 fts_set(NiL
, ent
, FTS_SKIP
);
146 error(2, "%s: cannot remove", ent
->fts_path
);
151 if (!state
->recursive
)
153 fts_set(NiL
, ent
, FTS_SKIP
);
154 error(2, "%s: directory", ent
->fts_path
);
159 if (state
->unconditional
&& (ent
->fts_statp
->st_mode
^ S_IRWXU
))
160 chmod(path
, (ent
->fts_statp
->st_mode
& S_IPERM
)|S_IRWXU
);
161 if (ent
->fts_level
> 0)
165 if (ent
->fts_accpath
== ent
->fts_name
|| !(s
= strrchr(ent
->fts_accpath
, '/')))
169 path
= ent
->fts_accpath
;
171 v
= !stat(path
, &st
);
175 v
= st
.st_nlink
<= 2 || st
.st_ino
== ent
->fts_parent
->fts_statp
->st_ino
&& st
.st_dev
== ent
->fts_parent
->fts_statp
->st_dev
|| strchr(astconf("PATH_ATTRIBUTES", path
, NiL
), 'l');
181 if (state
->interactive
)
183 if ((v
= astquery(-1, "remove directory %s? ", ent
->fts_path
)) < 0 || sh_checksig(state
->context
))
187 fts_set(NiL
, ent
, FTS_SKIP
);
191 if (ent
->fts_info
== FTS_D
)
196 ent
->fts_info
= FTS_DC
;
197 error(1, "%s: hard link to directory", ent
->fts_path
);
200 else if (ent
->fts_info
== FTS_D
)
204 if (isempty(ent
) || state
->directory
)
206 path
= ent
->fts_name
;
207 if (path
[0] != '.' || path
[1])
209 path
= ent
->fts_accpath
;
211 sfputr(sfstdout
, ent
->fts_path
, '\n');
212 if ((ent
->fts_info
== FTS_DC
|| state
->directory
) ? remove(path
) : rmdir(path
))
218 #if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST)
221 if (ent
->fts_info
== FTS_DP
&& !beenhere(ent
))
224 fts_set(NiL
, ent
, FTS_AGAIN
);
231 error(ERROR_SYSTEM
|2, "%s: directory not removed", ent
->fts_path
);
237 else if (!state
->force
)
238 error(2, "%s: cannot remove", ent
->fts_path
);
246 error(2, "%s: directory not removed", ent
->fts_path
);
252 path
= ent
->fts_accpath
;
254 sfputr(sfstdout
, ent
->fts_path
, '\n');
255 if (state
->interactive
)
257 if ((v
= astquery(-1, "remove %s? ", ent
->fts_path
)) < 0 || sh_checksig(state
->context
))
265 else if (!state
->force
&& state
->terminal
&& S_ISREG(ent
->fts_statp
->st_mode
))
267 if ((n
= open(path
, O_RDWR
)) < 0)
276 (v
= astquery(-1, "override protection %s for %s? ",
278 errno
== ETXTBSY
? "``running program''" :
280 ent
->fts_statp
->st_uid
!= state
->uid
? "``not owner''" :
281 fmtmode(ent
->fts_statp
->st_mode
& S_IPERM
, 0) + 1, ent
->fts_path
)) < 0 ||
282 sh_checksig(state
->context
))
294 if (state
->clobber
&& S_ISREG(ent
->fts_statp
->st_mode
) && ent
->fts_statp
->st_size
> 0)
296 if ((n
= open(path
, O_WRONLY
)) < 0)
297 error(ERROR_SYSTEM
|2, "%s: cannot clear data", ent
->fts_path
);
300 off_t c
= ent
->fts_statp
->st_size
;
304 if (write(n
, state
->buf
, sizeof(state
->buf
)) != sizeof(state
->buf
))
306 error(ERROR_SYSTEM
|2, "%s: data clear error", ent
->fts_path
);
309 if (c
<= sizeof(state
->buf
))
311 c
-= sizeof(state
->buf
);
326 if (!state
->force
|| state
->interactive
)
327 error(ERROR_SYSTEM
|2, "%s: not removed", ent
->fts_path
);
339 b_rm(int argc
, register char** argv
, void* context
)
346 cmdinit(argc
, argv
, context
, ERROR_CATALOG
, ERROR_NOTIFY
);
347 memset(&state
, 0, sizeof(state
));
348 state
.context
= context
;
349 state
.fs3d
= fs3d(FS3D_TEST
);
350 state
.terminal
= isatty(0);
353 switch (optget(argv
, usage
))
360 state
.interactive
= 0;
363 state
.interactive
= 1;
374 error(1, "%s not implemented on this system", opt_info
.name
);
378 state
.unconditional
= 1;
384 error(ERROR_USAGE
|4, "%s", opt_info
.arg
);
387 error(2, "%s", opt_info
.arg
);
392 argv
+= opt_info
.index
;
393 if (*argv
&& streq(*argv
, "-") && !streq(*(argv
- 1), "--"))
395 if (error_info
.errors
|| !*argv
)
396 error(ERROR_USAGE
|4, "%s", optusage(NiL
));
402 if (state
.interactive
)
404 state
.uid
= geteuid();
405 state
.unconditional
= state
.unconditional
&& state
.recursive
&& state
.force
;
406 if (state
.recursive
&& state
.fs3d
)
414 if (fts
= fts_open(argv
, FTS_PHYSICAL
, NiL
))
416 while (!sh_checksig(context
) && (ent
= fts_read(fts
)) && !rm(&state
, ent
));
419 else if (!state
.force
)
420 error(ERROR_SYSTEM
|2, "%s: cannot remove", argv
[0]);
423 return error_info
.errors
!= 0;