rm.c revision 193087
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1990, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 4. Neither the name of the University nor the names of its contributors 141556Srgrimes * may be used to endorse or promote products derived from this software 151556Srgrimes * without specific prior written permission. 161556Srgrimes * 171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271556Srgrimes * SUCH DAMAGE. 281556Srgrimes */ 291556Srgrimes 301556Srgrimes#if 0 311556Srgrimes#ifndef lint 323044Sdgstatic const char copyright[] = 3320421Ssteve"@(#) Copyright (c) 1990, 1993, 1994\n\ 341556Srgrimes The Regents of the University of California. All rights reserved.\n"; 351556Srgrimes#endif /* not lint */ 361556Srgrimes 3720421Ssteve#ifndef lint 381556Srgrimesstatic char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 391556Srgrimes#endif /* not lint */ 401556Srgrimes#endif 411556Srgrimes#include <sys/cdefs.h> 421556Srgrimes__FBSDID("$FreeBSD: head/bin/rm/rm.c 193087 2009-05-30 10:42:19Z jilles $"); 4320421Ssteve 441556Srgrimes#include <sys/stat.h> 451556Srgrimes#include <sys/param.h> 461556Srgrimes#include <sys/mount.h> 471556Srgrimes 481556Srgrimes#include <err.h> 491556Srgrimes#include <errno.h> 501556Srgrimes#include <fcntl.h> 511556Srgrimes#include <fts.h> 521556Srgrimes#include <grp.h> 531556Srgrimes#include <pwd.h> 541556Srgrimes#include <stdio.h> 551556Srgrimes#include <stdlib.h> 561556Srgrimes#include <string.h> 571556Srgrimes#include <sysexits.h> 5820421Ssteve#include <unistd.h> 5920421Ssteve 6020421Ssteveint dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 6120421Ssteveint rflag, Iflag; 6220421Ssteveuid_t uid; 6320421Sstevevolatile sig_atomic_t info; 647798Sache 657798Sacheint check(char *, char *, struct stat *); 6620421Ssteveint check2(char **); 671556Srgrimesvoid checkdot(char **); 6820421Sstevevoid checkslash(char **); 6920421Sstevevoid rm_file(char **); 7020421Ssteveint rm_overwrite(char *, struct stat *); 717798Sachevoid rm_tree(char **); 721556Srgrimesstatic void siginfo(int __unused); 731556Srgrimesvoid usage(void); 741556Srgrimes 751556Srgrimes/* 761556Srgrimes * rm -- 771556Srgrimes * This rm is different from historic rm's, but is expected to match 781556Srgrimes * POSIX 1003.2 behavior. The most visible difference is that -f 791556Srgrimes * has two specific effects now, ignore non-existent files and force 801556Srgrimes * file removal. 811556Srgrimes */ 821556Srgrimesint 831556Srgrimesmain(int argc, char *argv[]) 841556Srgrimes{ 851556Srgrimes int ch; 861556Srgrimes char *p; 871556Srgrimes 881556Srgrimes /* 891556Srgrimes * Test for the special case where the utility is called as 901556Srgrimes * "unlink", for which the functionality provided is greatly 911556Srgrimes * simplified. 921556Srgrimes */ 931556Srgrimes if ((p = rindex(argv[0], '/')) == NULL) 9420421Ssteve p = argv[0]; 9520421Ssteve else 9614148Spst ++p; 9720421Ssteve if (strcmp(p, "unlink") == 0) { 9820421Ssteve while (getopt(argc, argv, "") != -1) 9920421Ssteve usage(); 1001556Srgrimes argc -= optind; 1011556Srgrimes argv += optind; 1021556Srgrimes if (argc != 1) 1031556Srgrimes usage(); 1041556Srgrimes rm_file(&argv[0]); 1051556Srgrimes exit(eval); 1061556Srgrimes } 1071556Srgrimes 1081556Srgrimes Pflag = rflag = 0; 1091556Srgrimes while ((ch = getopt(argc, argv, "dfiIPRrvW")) != -1) 1101556Srgrimes switch(ch) { 1111556Srgrimes case 'd': 1121556Srgrimes dflag = 1; 1131556Srgrimes break; 1141556Srgrimes case 'f': 1151556Srgrimes fflag = 1; 1161556Srgrimes iflag = 0; 1171556Srgrimes break; 1181556Srgrimes case 'i': 11920421Ssteve fflag = 0; 12020421Ssteve iflag = 1; 12120421Ssteve break; 12220421Ssteve case 'I': 12320421Ssteve Iflag = 1; 1241556Srgrimes break; 1251556Srgrimes case 'P': 1261556Srgrimes Pflag = 1; 1271556Srgrimes break; 1281556Srgrimes case 'R': 1291556Srgrimes case 'r': /* Compatibility. */ 1301556Srgrimes rflag = 1; 1311556Srgrimes break; 1321556Srgrimes case 'v': 1331556Srgrimes vflag = 1; 1347798Sache break; 1351556Srgrimes case 'W': 13620421Ssteve Wflag = 1; 13720421Ssteve break; 13820421Ssteve default: 13920421Ssteve usage(); 14020421Ssteve } 14120421Ssteve argc -= optind; 14220421Ssteve argv += optind; 14320421Ssteve 14420421Ssteve if (argc < 1) { 1451556Srgrimes if (fflag) 1461556Srgrimes return (0); 1471556Srgrimes usage(); 1481556Srgrimes } 1491556Srgrimes 1501556Srgrimes checkdot(argv); 1511556Srgrimes if (getenv("POSIXLY_CORRECT") == NULL) 1521556Srgrimes checkslash(argv); 1531556Srgrimes uid = geteuid(); 1541556Srgrimes 15520421Ssteve (void)signal(SIGINFO, siginfo); 1567798Sache if (*argv) { 1571556Srgrimes stdin_ok = isatty(STDIN_FILENO); 1581556Srgrimes 1591556Srgrimes if (Iflag) { 1601556Srgrimes if (check2(argv) == 0) 1611556Srgrimes exit (1); 16220421Ssteve } 1631556Srgrimes if (rflag) 1641556Srgrimes rm_tree(argv); 1651556Srgrimes else 1661556Srgrimes rm_file(argv); 1671556Srgrimes } 1681556Srgrimes 1691556Srgrimes exit (eval); 17020421Ssteve} 17120421Ssteve 17220421Sstevevoid 17320421Ssteverm_tree(char **argv) 17420421Ssteve{ 17520421Ssteve FTS *fts; 17620421Ssteve FTSENT *p; 17720421Ssteve int needstat; 1781556Srgrimes int flags; 1791556Srgrimes int rval; 1801556Srgrimes 1811556Srgrimes /* 1821556Srgrimes * Remove a file hierarchy. If forcing removal (-f), or interactive 1831556Srgrimes * (-i) or can't ask anyway (stdin_ok), don't stat the file. 1841556Srgrimes */ 1851556Srgrimes needstat = !uid || (!fflag && !iflag && stdin_ok); 1861556Srgrimes 1871556Srgrimes /* 1881556Srgrimes * If the -i option is specified, the user can skip on the pre-order 1891556Srgrimes * visit. The fts_number field flags skipped directories. 1901556Srgrimes */ 1911556Srgrimes#define SKIPPED 1 1921556Srgrimes 1931556Srgrimes flags = FTS_PHYSICAL; 1941556Srgrimes if (!needstat) 1951556Srgrimes flags |= FTS_NOSTAT; 1961556Srgrimes if (Wflag) 1971556Srgrimes flags |= FTS_WHITEOUT; 1981556Srgrimes if (!(fts = fts_open(argv, flags, NULL))) { 1991556Srgrimes if (fflag && errno == ENOENT) 2001556Srgrimes return; 2011556Srgrimes err(1, "fts_open"); 2021556Srgrimes } 2031556Srgrimes while ((p = fts_read(fts)) != NULL) { 2041556Srgrimes switch (p->fts_info) { 20520421Ssteve case FTS_DNR: 2061556Srgrimes if (!fflag || p->fts_errno != ENOENT) { 2071556Srgrimes warnx("%s: %s", 2081556Srgrimes p->fts_path, strerror(p->fts_errno)); 2091556Srgrimes eval = 1; 2107798Sache } 2117798Sache continue; 2127798Sache case FTS_ERR: 2137798Sache errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 2147798Sache case FTS_NS: 2157798Sache /* 2161556Srgrimes * Assume that since fts_read() couldn't stat the 2171556Srgrimes * file, it can't be unlinked. 2181556Srgrimes */ 2191556Srgrimes if (!needstat) 2201556Srgrimes break; 2211556Srgrimes if (!fflag || p->fts_errno != ENOENT) { 22220421Ssteve warnx("%s: %s", 22320421Ssteve p->fts_path, strerror(p->fts_errno)); 22420421Ssteve eval = 1; 22520421Ssteve } 2261556Srgrimes continue; 2271556Srgrimes case FTS_D: 2287798Sache /* Pre-order: give user chance to skip. */ 2297798Sache if (!fflag && !check(p->fts_path, p->fts_accpath, 2307798Sache p->fts_statp)) { 2317798Sache (void)fts_set(fts, p, FTS_SKIP); 2327798Sache p->fts_number = SKIPPED; 2337798Sache } 2347798Sache else if (!uid && 2357798Sache (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 2367798Sache !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 2377798Sache lchflags(p->fts_accpath, 2387798Sache p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 2397798Sache goto err; 24020421Ssteve continue; 24120421Ssteve case FTS_DP: 24220421Ssteve /* Post-order: see if user skipped. */ 24320421Ssteve if (p->fts_number == SKIPPED) 2441556Srgrimes continue; 24520421Ssteve break; 24620421Ssteve default: 24720421Ssteve if (!fflag && 24820421Ssteve !check(p->fts_path, p->fts_accpath, p->fts_statp)) 24920421Ssteve continue; 25020421Ssteve } 25120421Ssteve 25220421Ssteve rval = 0; 25320421Ssteve if (!uid && 25420421Ssteve (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 25520421Ssteve !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 2567798Sache rval = lchflags(p->fts_accpath, 2577798Sache p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 2587798Sache if (rval == 0) { 2597798Sache /* 2607798Sache * If we can't read or search the directory, may still be 2611556Srgrimes * able to remove it. Don't print out the un{read,search}able 2627798Sache * message unless the remove fails. 2631556Srgrimes */ 2641556Srgrimes switch (p->fts_info) { 2651556Srgrimes case FTS_DP: 2661556Srgrimes case FTS_DNR: 2671556Srgrimes rval = rmdir(p->fts_accpath); 2681556Srgrimes if (rval == 0 || (fflag && errno == ENOENT)) { 2691556Srgrimes if (rval == 0 && vflag) 2701556Srgrimes (void)printf("%s\n", 2711556Srgrimes p->fts_path); 2721556Srgrimes if (rval == 0 && info) { 2731556Srgrimes info = 0; 2741556Srgrimes (void)printf("%s\n", 27520421Ssteve p->fts_path); 2761556Srgrimes } 2771556Srgrimes continue; 2781556Srgrimes } 2791556Srgrimes break; 2801556Srgrimes 2811556Srgrimes case FTS_W: 2821556Srgrimes rval = undelete(p->fts_accpath); 2831556Srgrimes if (rval == 0 && (fflag && errno == ENOENT)) { 2841556Srgrimes if (vflag) 28520421Ssteve (void)printf("%s\n", 2861556Srgrimes p->fts_path); 2871556Srgrimes if (info) { 2881556Srgrimes info = 0; 28920421Ssteve (void)printf("%s\n", 29020421Ssteve p->fts_path); 29120421Ssteve } 29220421Ssteve continue; 29320421Ssteve } 29420421Ssteve break; 29520421Ssteve 29620421Ssteve case FTS_NS: 29720421Ssteve /* 29820421Ssteve * Assume that since fts_read() couldn't stat 2991556Srgrimes * the file, it can't be unlinked. 30020421Ssteve */ 30120421Ssteve if (fflag) 30220421Ssteve continue; 30320421Ssteve /* FALLTHROUGH */ 3041556Srgrimes default: 3051556Srgrimes if (Pflag) 30620421Ssteve if (!rm_overwrite(p->fts_accpath, NULL)) 30720421Ssteve continue; 3081556Srgrimes rval = unlink(p->fts_accpath); 3091556Srgrimes if (rval == 0 || (fflag && errno == ENOENT)) { 3101556Srgrimes if (rval == 0 && vflag) 3111556Srgrimes (void)printf("%s\n", 31220421Ssteve p->fts_path); 3131556Srgrimes if (rval == 0 && info) { 31420421Ssteve info = 0; 31520421Ssteve (void)printf("%s\n", 31620421Ssteve p->fts_path); 3171556Srgrimes } 3187798Sache continue; 3197798Sache } 3207798Sache } 3217798Sache } 3227798Sacheerr: 3237798Sache warn("%s", p->fts_path); 32420421Ssteve eval = 1; 3257798Sache } 32620421Ssteve if (errno) 32720421Ssteve err(1, "fts_read"); 32820421Ssteve fts_close(fts); 32920421Ssteve} 33020421Ssteve 3317798Sachevoid 3327798Sacherm_file(char **argv) 3337798Sache{ 3347798Sache struct stat sb; 3357798Sache int rval; 3367798Sache char *f; 3371556Srgrimes 3381556Srgrimes /* 3391556Srgrimes * Remove a file. POSIX 1003.2 states that, by default, attempting 3401556Srgrimes * to remove a directory is an error, so must always stat the file. 3411556Srgrimes */ 3421556Srgrimes while ((f = *argv++) != NULL) { 3431556Srgrimes /* Assume if can't stat the file, can't unlink it. */ 3441556Srgrimes if (lstat(f, &sb)) { 3451556Srgrimes if (Wflag) { 3461556Srgrimes sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 3471556Srgrimes } else { 3481556Srgrimes if (!fflag || errno != ENOENT) { 3491556Srgrimes warn("%s", f); 3501556Srgrimes eval = 1; 3511556Srgrimes } 3521556Srgrimes continue; 3531556Srgrimes } 3541556Srgrimes } else if (Wflag) { 3551556Srgrimes warnx("%s: %s", f, strerror(EEXIST)); 3561556Srgrimes eval = 1; 3571556Srgrimes continue; 3581556Srgrimes } 3591556Srgrimes 3601556Srgrimes if (S_ISDIR(sb.st_mode) && !dflag) { 3611556Srgrimes warnx("%s: is a directory", f); 3621556Srgrimes eval = 1; 3631556Srgrimes continue; 3641556Srgrimes } 3651556Srgrimes if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 3661556Srgrimes continue; 3671556Srgrimes rval = 0; 3681556Srgrimes if (!uid && !S_ISWHT(sb.st_mode) && 3691556Srgrimes (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 3701556Srgrimes !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 3711556Srgrimes rval = lchflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 3721556Srgrimes if (rval == 0) { 3731556Srgrimes if (S_ISWHT(sb.st_mode)) 3741556Srgrimes rval = undelete(f); 3751556Srgrimes else if (S_ISDIR(sb.st_mode)) 3761556Srgrimes rval = rmdir(f); 3771556Srgrimes else { 3781556Srgrimes if (Pflag) 3791556Srgrimes if (!rm_overwrite(f, &sb)) 3801556Srgrimes continue; 3811556Srgrimes rval = unlink(f); 3821556Srgrimes } 3831556Srgrimes } 3841556Srgrimes if (rval && (!fflag || errno != ENOENT)) { 3851556Srgrimes warn("%s", f); 3861556Srgrimes eval = 1; 3871556Srgrimes } 3881556Srgrimes if (vflag && rval == 0) 3891556Srgrimes (void)printf("%s\n", f); 3901556Srgrimes if (info && rval == 0) { 3911556Srgrimes info = 0; 3921556Srgrimes (void)printf("%s\n", f); 3931556Srgrimes } 3941556Srgrimes } 3951556Srgrimes} 3961556Srgrimes 3971556Srgrimes/* 3981556Srgrimes * rm_overwrite -- 3991556Srgrimes * Overwrite the file 3 times with varying bit patterns. 4001556Srgrimes * 4011556Srgrimes * XXX 4021556Srgrimes * This is a cheap way to *really* delete files. Note that only regular 4031556Srgrimes * files are deleted, directories (and therefore names) will remain. 4041556Srgrimes * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 4051556Srgrimes * System V file system). In a logging file system, you'll have to have 4067798Sache * kernel support. 4071556Srgrimes */ 4081556Srgrimesint 4091556Srgrimesrm_overwrite(char *file, struct stat *sbp) 4101556Srgrimes{ 4111556Srgrimes struct stat sb; 4121556Srgrimes struct statfs fsb; 4131556Srgrimes off_t len; 4141556Srgrimes int bsize, fd, wlen; 4151556Srgrimes char *buf = NULL; 4161556Srgrimes 4171556Srgrimes fd = -1; 4187798Sache if (sbp == NULL) { 41920421Ssteve if (lstat(file, &sb)) 4207798Sache goto err; 42120421Ssteve sbp = &sb; 4221556Srgrimes } 4231556Srgrimes if (!S_ISREG(sbp->st_mode)) 4247798Sache return (1); 4257798Sache if (sbp->st_nlink > 1 && !fflag) { 4267798Sache warnx("%s (inode %u): not overwritten due to multiple links", 4277798Sache file, sbp->st_ino); 4281556Srgrimes return (0); 4291556Srgrimes } 4307798Sache if ((fd = open(file, O_WRONLY, 0)) == -1) 4317798Sache goto err; 4327798Sache if (fstatfs(fd, &fsb) == -1) 4331556Srgrimes goto err; 4341556Srgrimes bsize = MAX(fsb.f_iosize, 1024); 4351556Srgrimes if ((buf = malloc(bsize)) == NULL) 4361556Srgrimes err(1, "%s: malloc", file); 4371556Srgrimes 4381556Srgrimes#define PASS(byte) { \ 43914409Swosch memset(buf, byte, bsize); \ 4401556Srgrimes for (len = sbp->st_size; len > 0; len -= wlen) { \ 4411556Srgrimes wlen = len < bsize ? len : bsize; \ 4422927Sphk if (write(fd, buf, wlen) != wlen) \ 4431556Srgrimes goto err; \ 4441556Srgrimes } \ 4451556Srgrimes} 4461556Srgrimes PASS(0xff); 4471556Srgrimes if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 4481556Srgrimes goto err; 4491556Srgrimes PASS(0x00); 4501556Srgrimes if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 4511556Srgrimes goto err; 4521556Srgrimes PASS(0xff); 4531556Srgrimes if (!fsync(fd) && !close(fd)) { 4541556Srgrimes free(buf); 4551556Srgrimes return (1); 4561556Srgrimes } 4571556Srgrimes 4581556Srgrimeserr: eval = 1; 4591556Srgrimes if (buf) 46020421Ssteve free(buf); 46120421Ssteve if (fd != -1) 4621556Srgrimes close(fd); 4631556Srgrimes warn("%s", file); 4641556Srgrimes return (0); 4651556Srgrimes} 4661556Srgrimes 4671556Srgrimes 4681556Srgrimesint 4691556Srgrimescheck(char *path, char *name, struct stat *sp) 4701556Srgrimes{ 4711556Srgrimes int ch, first; 47220421Ssteve char modep[15], *flagsp; 47314409Swosch 47420421Ssteve /* Check -i first. */ 47520421Ssteve if (iflag) 47620421Ssteve (void)fprintf(stderr, "remove %s? ", path); 4771556Srgrimes else { 4781556Srgrimes /* 479 * If it's not a symbolic link and it's unwritable and we're 480 * talking to a terminal, ask. Symbolic links are excluded 481 * because their permissions are meaningless. Check stdin_ok 482 * first because we may not have stat'ed the file. 483 */ 484 if (!stdin_ok || S_ISLNK(sp->st_mode) || 485 (!access(name, W_OK) && 486 !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 487 (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 488 return (1); 489 strmode(sp->st_mode, modep); 490 if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 491 err(1, "fflagstostr"); 492 if (Pflag) 493 errx(1, 494 "%s: -P was specified, but file is not writable", 495 path); 496 (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 497 modep + 1, modep[9] == ' ' ? "" : " ", 498 user_from_uid(sp->st_uid, 0), 499 group_from_gid(sp->st_gid, 0), 500 *flagsp ? flagsp : "", *flagsp ? " " : "", 501 path); 502 free(flagsp); 503 } 504 (void)fflush(stderr); 505 506 first = ch = getchar(); 507 while (ch != '\n' && ch != EOF) 508 ch = getchar(); 509 return (first == 'y' || first == 'Y'); 510} 511 512#define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0') 513void 514checkslash(char **argv) 515{ 516 char **t, **u; 517 int complained; 518 519 complained = 0; 520 for (t = argv; *t;) { 521 if (ISSLASH(*t)) { 522 if (!complained++) 523 warnx("\"/\" may not be removed"); 524 eval = 1; 525 for (u = t; u[0] != NULL; ++u) 526 u[0] = u[1]; 527 } else { 528 ++t; 529 } 530 } 531} 532 533int 534check2(char **argv) 535{ 536 struct stat st; 537 int first; 538 int ch; 539 int fcount = 0; 540 int dcount = 0; 541 int i; 542 const char *dname = NULL; 543 544 for (i = 0; argv[i]; ++i) { 545 if (lstat(argv[i], &st) == 0) { 546 if (S_ISDIR(st.st_mode)) { 547 ++dcount; 548 dname = argv[i]; /* only used if 1 dir */ 549 } else { 550 ++fcount; 551 } 552 } 553 } 554 first = 0; 555 while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') { 556 if (dcount && rflag) { 557 fprintf(stderr, "recursively remove"); 558 if (dcount == 1) 559 fprintf(stderr, " %s", dname); 560 else 561 fprintf(stderr, " %d dirs", dcount); 562 if (fcount == 1) 563 fprintf(stderr, " and 1 file"); 564 else if (fcount > 1) 565 fprintf(stderr, " and %d files", fcount); 566 } else if (dcount + fcount > 3) { 567 fprintf(stderr, "remove %d files", dcount + fcount); 568 } else { 569 return(1); 570 } 571 fprintf(stderr, "? "); 572 fflush(stderr); 573 574 first = ch = getchar(); 575 while (ch != '\n' && ch != EOF) 576 ch = getchar(); 577 if (ch == EOF) 578 break; 579 } 580 return (first == 'y' || first == 'Y'); 581} 582 583#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 584void 585checkdot(char **argv) 586{ 587 char *p, **save, **t; 588 int complained; 589 590 complained = 0; 591 for (t = argv; *t;) { 592 if ((p = strrchr(*t, '/')) != NULL) 593 ++p; 594 else 595 p = *t; 596 if (ISDOT(p)) { 597 if (!complained++) 598 warnx("\".\" and \"..\" may not be removed"); 599 eval = 1; 600 for (save = t; (t[0] = t[1]) != NULL; ++t) 601 continue; 602 t = save; 603 } else 604 ++t; 605 } 606} 607 608void 609usage(void) 610{ 611 612 (void)fprintf(stderr, "%s\n%s\n", 613 "usage: rm [-f | -i] [-dIPRrvW] file ...", 614 " unlink file"); 615 exit(EX_USAGE); 616} 617 618static void 619siginfo(int sig __unused) 620{ 621 622 info = 1; 623} 624