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 30114433Sobrien#if 0 311556Srgrimes#ifndef lint 3227959Sstevestatic const char copyright[] = 331556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\ 341556Srgrimes The Regents of the University of California. All rights reserved.\n"; 3527964Ssteve#endif /* not lint */ 3627964Ssteve 3727964Ssteve#ifndef lint 3827964Sstevestatic char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; 39114433Sobrien#endif /* not lint */ 4027964Ssteve#endif 4199110Sobrien#include <sys/cdefs.h> 4299110Sobrien__FBSDID("$FreeBSD$"); 431556Srgrimes 441556Srgrimes#include <sys/stat.h> 4547584Skris#include <sys/param.h> 4647584Skris#include <sys/mount.h> 471556Srgrimes 481556Srgrimes#include <err.h> 491556Srgrimes#include <errno.h> 501556Srgrimes#include <fcntl.h> 511556Srgrimes#include <fts.h> 5290644Simp#include <grp.h> 53287791Sdelphij#include <locale.h> 5490644Simp#include <pwd.h> 55241014Smdf#include <stdint.h> 561556Srgrimes#include <stdio.h> 571556Srgrimes#include <stdlib.h> 581556Srgrimes#include <string.h> 5950539Smharo#include <sysexits.h> 601556Srgrimes#include <unistd.h> 611556Srgrimes 62226961Sedstatic int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; 63249948Seadlerstatic int rflag, Iflag, xflag; 64226961Sedstatic uid_t uid; 65226961Sedstatic volatile sig_atomic_t info; 661556Srgrimes 67249949Seadlerstatic int check(const char *, const char *, struct stat *); 68249949Seadlerstatic int check2(char **); 69249949Seadlerstatic void checkdot(char **); 70249949Seadlerstatic void checkslash(char **); 71249949Seadlerstatic void rm_file(char **); 72249949Seadlerstatic int rm_overwrite(const char *, struct stat *); 73249949Seadlerstatic void rm_tree(char **); 74191670Simpstatic void siginfo(int __unused); 75249949Seadlerstatic void usage(void); 761556Srgrimes 771556Srgrimes/* 781556Srgrimes * rm -- 791556Srgrimes * This rm is different from historic rm's, but is expected to match 80136112Sdes * POSIX 1003.2 behavior. The most visible difference is that -f 811556Srgrimes * has two specific effects now, ignore non-existent files and force 82136112Sdes * file removal. 831556Srgrimes */ 841556Srgrimesint 8590110Simpmain(int argc, char *argv[]) 861556Srgrimes{ 87137009Sdelphij int ch; 8854895Ssheldonh char *p; 891556Srgrimes 90287791Sdelphij (void)setlocale(LC_ALL, ""); 91287791Sdelphij 9254895Ssheldonh /* 9354895Ssheldonh * Test for the special case where the utility is called as 9454895Ssheldonh * "unlink", for which the functionality provided is greatly 9554895Ssheldonh * simplified. 9654895Ssheldonh */ 97219680Sjilles if ((p = strrchr(argv[0], '/')) == NULL) 9854895Ssheldonh p = argv[0]; 9954895Ssheldonh else 10054895Ssheldonh ++p; 10154895Ssheldonh if (strcmp(p, "unlink") == 0) { 10297533Stjr while (getopt(argc, argv, "") != -1) 10354895Ssheldonh usage(); 10497533Stjr argc -= optind; 10597533Stjr argv += optind; 10699858Stjr if (argc != 1) 10797533Stjr usage(); 10897533Stjr rm_file(&argv[0]); 10997533Stjr exit(eval); 11054895Ssheldonh } 11154895Ssheldonh 112249948Seadler Pflag = rflag = xflag = 0; 113249948Seadler while ((ch = getopt(argc, argv, "dfiIPRrvWx")) != -1) 1141556Srgrimes switch(ch) { 1151556Srgrimes case 'd': 1161556Srgrimes dflag = 1; 1171556Srgrimes break; 1181556Srgrimes case 'f': 1191556Srgrimes fflag = 1; 1201556Srgrimes iflag = 0; 1211556Srgrimes break; 1221556Srgrimes case 'i': 1231556Srgrimes fflag = 0; 1241556Srgrimes iflag = 1; 1251556Srgrimes break; 126137009Sdelphij case 'I': 127137009Sdelphij Iflag = 1; 128137009Sdelphij break; 1291556Srgrimes case 'P': 1301556Srgrimes Pflag = 1; 1311556Srgrimes break; 1321556Srgrimes case 'R': 1331556Srgrimes case 'r': /* Compatibility. */ 1341556Srgrimes rflag = 1; 1351556Srgrimes break; 13650872Smharo case 'v': 13750872Smharo vflag = 1; 13850872Smharo break; 13920421Ssteve case 'W': 14020421Ssteve Wflag = 1; 14120421Ssteve break; 142249948Seadler case 'x': 143249948Seadler xflag = 1; 144249948Seadler break; 1451556Srgrimes default: 1461556Srgrimes usage(); 1471556Srgrimes } 1481556Srgrimes argc -= optind; 1491556Srgrimes argv += optind; 1501556Srgrimes 15144282Sjkh if (argc < 1) { 15244282Sjkh if (fflag) 153122409Sguido return (0); 1541556Srgrimes usage(); 15544282Sjkh } 1561556Srgrimes 1571556Srgrimes checkdot(argv); 158290634Sbapt checkslash(argv); 1597798Sache uid = geteuid(); 1601556Srgrimes 161191670Simp (void)signal(SIGINFO, siginfo); 16220421Ssteve if (*argv) { 16320421Ssteve stdin_ok = isatty(STDIN_FILENO); 16420421Ssteve 165137009Sdelphij if (Iflag) { 166137009Sdelphij if (check2(argv) == 0) 167137009Sdelphij exit (1); 168137009Sdelphij } 16920421Ssteve if (rflag) 17020421Ssteve rm_tree(argv); 17120421Ssteve else 17220421Ssteve rm_file(argv); 17320421Ssteve } 17420421Ssteve 1751556Srgrimes exit (eval); 1761556Srgrimes} 1771556Srgrimes 178249949Seadlerstatic void 17990110Simprm_tree(char **argv) 1801556Srgrimes{ 1811556Srgrimes FTS *fts; 1821556Srgrimes FTSENT *p; 1831556Srgrimes int needstat; 18420421Ssteve int flags; 1857798Sache int rval; 1861556Srgrimes 1871556Srgrimes /* 1881556Srgrimes * Remove a file hierarchy. If forcing removal (-f), or interactive 1891556Srgrimes * (-i) or can't ask anyway (stdin_ok), don't stat the file. 1901556Srgrimes */ 19120421Ssteve needstat = !uid || (!fflag && !iflag && stdin_ok); 1921556Srgrimes 1931556Srgrimes /* 1941556Srgrimes * If the -i option is specified, the user can skip on the pre-order 1951556Srgrimes * visit. The fts_number field flags skipped directories. 1961556Srgrimes */ 1971556Srgrimes#define SKIPPED 1 1981556Srgrimes 19951230Sbde flags = FTS_PHYSICAL; 20020421Ssteve if (!needstat) 20120421Ssteve flags |= FTS_NOSTAT; 20220421Ssteve if (Wflag) 20320421Ssteve flags |= FTS_WHITEOUT; 204249948Seadler if (xflag) 205249948Seadler flags |= FTS_XDEV; 206137639Sjkh if (!(fts = fts_open(argv, flags, NULL))) { 207137639Sjkh if (fflag && errno == ENOENT) 208137639Sjkh return; 20999744Sdillon err(1, "fts_open"); 210137639Sjkh } 2111556Srgrimes while ((p = fts_read(fts)) != NULL) { 2121556Srgrimes switch (p->fts_info) { 2131556Srgrimes case FTS_DNR: 2141556Srgrimes if (!fflag || p->fts_errno != ENOENT) { 2151556Srgrimes warnx("%s: %s", 2161556Srgrimes p->fts_path, strerror(p->fts_errno)); 2171556Srgrimes eval = 1; 2181556Srgrimes } 2191556Srgrimes continue; 2201556Srgrimes case FTS_ERR: 2211556Srgrimes errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); 2221556Srgrimes case FTS_NS: 2231556Srgrimes /* 224124041Skuriyama * Assume that since fts_read() couldn't stat the 225124041Skuriyama * file, it can't be unlinked. 2261556Srgrimes */ 2271556Srgrimes if (!needstat) 2281556Srgrimes break; 2291556Srgrimes if (!fflag || p->fts_errno != ENOENT) { 2301556Srgrimes warnx("%s: %s", 2311556Srgrimes p->fts_path, strerror(p->fts_errno)); 2321556Srgrimes eval = 1; 2331556Srgrimes } 2341556Srgrimes continue; 2351556Srgrimes case FTS_D: 2361556Srgrimes /* Pre-order: give user chance to skip. */ 23720421Ssteve if (!fflag && !check(p->fts_path, p->fts_accpath, 2381556Srgrimes p->fts_statp)) { 2391556Srgrimes (void)fts_set(fts, p, FTS_SKIP); 2401556Srgrimes p->fts_number = SKIPPED; 2411556Srgrimes } 2427798Sache else if (!uid && 2437798Sache (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 2447798Sache !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 245193087Sjilles lchflags(p->fts_accpath, 2467798Sache p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) 2477798Sache goto err; 2481556Srgrimes continue; 2491556Srgrimes case FTS_DP: 2501556Srgrimes /* Post-order: see if user skipped. */ 2511556Srgrimes if (p->fts_number == SKIPPED) 2521556Srgrimes continue; 2531556Srgrimes break; 25420421Ssteve default: 25520421Ssteve if (!fflag && 25620421Ssteve !check(p->fts_path, p->fts_accpath, p->fts_statp)) 25720421Ssteve continue; 2581556Srgrimes } 2591556Srgrimes 2607798Sache rval = 0; 2617798Sache if (!uid && 2627798Sache (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 2637798Sache !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) 264193087Sjilles rval = lchflags(p->fts_accpath, 2657798Sache p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 26653819Smharo if (rval == 0) { 2677798Sache /* 2687798Sache * If we can't read or search the directory, may still be 2697798Sache * able to remove it. Don't print out the un{read,search}able 2707798Sache * message unless the remove fails. 2717798Sache */ 27220421Ssteve switch (p->fts_info) { 27320421Ssteve case FTS_DP: 27420421Ssteve case FTS_DNR: 27553819Smharo rval = rmdir(p->fts_accpath); 27653819Smharo if (rval == 0 || (fflag && errno == ENOENT)) { 27753819Smharo if (rval == 0 && vflag) 27850872Smharo (void)printf("%s\n", 27970219Sobrien p->fts_path); 280191670Simp if (rval == 0 && info) { 281191670Simp info = 0; 282191670Simp (void)printf("%s\n", 283191670Simp p->fts_path); 284191670Simp } 2851556Srgrimes continue; 28650539Smharo } 28720421Ssteve break; 28820421Ssteve 28920421Ssteve case FTS_W: 29053819Smharo rval = undelete(p->fts_accpath); 29153819Smharo if (rval == 0 && (fflag && errno == ENOENT)) { 29253819Smharo if (vflag) 29350872Smharo (void)printf("%s\n", 29470219Sobrien p->fts_path); 295191670Simp if (info) { 296191670Simp info = 0; 297191670Simp (void)printf("%s\n", 298191670Simp p->fts_path); 299191670Simp } 30020421Ssteve continue; 30150539Smharo } 30220421Ssteve break; 30320421Ssteve 304124041Skuriyama case FTS_NS: 305124041Skuriyama /* 306124041Skuriyama * Assume that since fts_read() couldn't stat 307124041Skuriyama * the file, it can't be unlinked. 308124041Skuriyama */ 309124041Skuriyama if (fflag) 310124041Skuriyama continue; 311124041Skuriyama /* FALLTHROUGH */ 312237339Sdelphij 313237339Sdelphij case FTS_F: 314237339Sdelphij case FTS_NSOK: 3157798Sache if (Pflag) 316237339Sdelphij if (!rm_overwrite(p->fts_accpath, p->fts_info == 317237339Sdelphij FTS_NSOK ? NULL : p->fts_statp)) 318122409Sguido continue; 319237339Sdelphij /* FALLTHROUGH */ 320237339Sdelphij 321237339Sdelphij default: 32253819Smharo rval = unlink(p->fts_accpath); 32353819Smharo if (rval == 0 || (fflag && errno == ENOENT)) { 32453819Smharo if (rval == 0 && vflag) 32550872Smharo (void)printf("%s\n", 32670219Sobrien p->fts_path); 327191670Simp if (rval == 0 && info) { 328191670Simp info = 0; 329191670Simp (void)printf("%s\n", 330191670Simp p->fts_path); 331191670Simp } 3327798Sache continue; 33350539Smharo } 3347798Sache } 3351556Srgrimes } 3367798Sacheerr: 3371556Srgrimes warn("%s", p->fts_path); 3381556Srgrimes eval = 1; 3391556Srgrimes } 340272372Sgjb if (!fflag && errno) 3411556Srgrimes err(1, "fts_read"); 342157770Smaxim fts_close(fts); 3431556Srgrimes} 3441556Srgrimes 345249950Seadlerstatic void 34690110Simprm_file(char **argv) 3471556Srgrimes{ 3481556Srgrimes struct stat sb; 34920421Ssteve int rval; 3501556Srgrimes char *f; 3511556Srgrimes 3521556Srgrimes /* 3531556Srgrimes * Remove a file. POSIX 1003.2 states that, by default, attempting 3541556Srgrimes * to remove a directory is an error, so must always stat the file. 3551556Srgrimes */ 3561556Srgrimes while ((f = *argv++) != NULL) { 3571556Srgrimes /* Assume if can't stat the file, can't unlink it. */ 3581556Srgrimes if (lstat(f, &sb)) { 35920421Ssteve if (Wflag) { 36020421Ssteve sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; 36120421Ssteve } else { 36220421Ssteve if (!fflag || errno != ENOENT) { 36320421Ssteve warn("%s", f); 36420421Ssteve eval = 1; 36520421Ssteve } 36620421Ssteve continue; 3671556Srgrimes } 36820421Ssteve } else if (Wflag) { 36920421Ssteve warnx("%s: %s", f, strerror(EEXIST)); 37020421Ssteve eval = 1; 3711556Srgrimes continue; 3721556Srgrimes } 37320421Ssteve 37420421Ssteve if (S_ISDIR(sb.st_mode) && !dflag) { 3751556Srgrimes warnx("%s: is a directory", f); 3761556Srgrimes eval = 1; 3771556Srgrimes continue; 3781556Srgrimes } 37920421Ssteve if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) 3801556Srgrimes continue; 3817798Sache rval = 0; 382163485Smaxim if (!uid && !S_ISWHT(sb.st_mode) && 3837798Sache (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && 3847798Sache !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) 385193087Sjilles rval = lchflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); 38653819Smharo if (rval == 0) { 38720421Ssteve if (S_ISWHT(sb.st_mode)) 38820421Ssteve rval = undelete(f); 38920421Ssteve else if (S_ISDIR(sb.st_mode)) 3907798Sache rval = rmdir(f); 3917798Sache else { 3927798Sache if (Pflag) 393122409Sguido if (!rm_overwrite(f, &sb)) 394122409Sguido continue; 3957798Sache rval = unlink(f); 3967798Sache } 3971556Srgrimes } 3981556Srgrimes if (rval && (!fflag || errno != ENOENT)) { 3991556Srgrimes warn("%s", f); 4001556Srgrimes eval = 1; 4011556Srgrimes } 40253819Smharo if (vflag && rval == 0) 40350539Smharo (void)printf("%s\n", f); 404191670Simp if (info && rval == 0) { 405191670Simp info = 0; 406191670Simp (void)printf("%s\n", f); 407191670Simp } 4081556Srgrimes } 4091556Srgrimes} 4101556Srgrimes 4111556Srgrimes/* 4121556Srgrimes * rm_overwrite -- 4131556Srgrimes * Overwrite the file 3 times with varying bit patterns. 4141556Srgrimes * 4151556Srgrimes * XXX 4161556Srgrimes * This is a cheap way to *really* delete files. Note that only regular 4171556Srgrimes * files are deleted, directories (and therefore names) will remain. 418102230Strhodes * Also, this assumes a fixed-block file system (like FFS, or a V7 or a 419213582Suqs * System V file system). In a logging or COW file system, you'll have to 420213582Suqs * have kernel support. 4211556Srgrimes */ 422249950Seadlerstatic int 423249949Seadlerrm_overwrite(const char *file, struct stat *sbp) 4241556Srgrimes{ 425237284Skevlo struct stat sb, sb2; 42647584Skris struct statfs fsb; 4271556Srgrimes off_t len; 42847584Skris int bsize, fd, wlen; 42947584Skris char *buf = NULL; 4301556Srgrimes 4311556Srgrimes fd = -1; 4321556Srgrimes if (sbp == NULL) { 4331556Srgrimes if (lstat(file, &sb)) 4341556Srgrimes goto err; 4351556Srgrimes sbp = &sb; 4361556Srgrimes } 4371556Srgrimes if (!S_ISREG(sbp->st_mode)) 438122409Sguido return (1); 439163812Sdelphij if (sbp->st_nlink > 1 && !fflag) { 440241014Smdf warnx("%s (inode %ju): not overwritten due to multiple links", 441241014Smdf file, (uintmax_t)sbp->st_ino); 442163812Sdelphij return (0); 443163777Sdelphij } 444237284Skevlo if ((fd = open(file, O_WRONLY|O_NONBLOCK|O_NOFOLLOW, 0)) == -1) 4451556Srgrimes goto err; 446237284Skevlo if (fstat(fd, &sb2)) 447237284Skevlo goto err; 448237284Skevlo if (sb2.st_dev != sbp->st_dev || sb2.st_ino != sbp->st_ino || 449237284Skevlo !S_ISREG(sb2.st_mode)) { 450237284Skevlo errno = EPERM; 451237284Skevlo goto err; 452237284Skevlo } 45347584Skris if (fstatfs(fd, &fsb) == -1) 45447584Skris goto err; 45547584Skris bsize = MAX(fsb.f_iosize, 1024); 45647584Skris if ((buf = malloc(bsize)) == NULL) 457122304Sbde err(1, "%s: malloc", file); 4581556Srgrimes 4591556Srgrimes#define PASS(byte) { \ 46047584Skris memset(buf, byte, bsize); \ 4611556Srgrimes for (len = sbp->st_size; len > 0; len -= wlen) { \ 46247584Skris wlen = len < bsize ? len : bsize; \ 4631556Srgrimes if (write(fd, buf, wlen) != wlen) \ 4641556Srgrimes goto err; \ 4651556Srgrimes } \ 4661556Srgrimes} 4671556Srgrimes PASS(0xff); 4681556Srgrimes if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 4691556Srgrimes goto err; 4701556Srgrimes PASS(0x00); 4711556Srgrimes if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) 4721556Srgrimes goto err; 4731556Srgrimes PASS(0xff); 47447584Skris if (!fsync(fd) && !close(fd)) { 47547584Skris free(buf); 476122409Sguido return (1); 47747584Skris } 4781556Srgrimes 4791556Srgrimeserr: eval = 1; 48047584Skris if (buf) 48147584Skris free(buf); 482122304Sbde if (fd != -1) 483122304Sbde close(fd); 4841556Srgrimes warn("%s", file); 485122409Sguido return (0); 4861556Srgrimes} 4871556Srgrimes 4881556Srgrimes 489249949Seadlerstatic int 490249949Seadlercheck(const char *path, const char *name, struct stat *sp) 4911556Srgrimes{ 4921556Srgrimes int ch, first; 49361749Sjoe char modep[15], *flagsp; 4941556Srgrimes 4951556Srgrimes /* Check -i first. */ 4961556Srgrimes if (iflag) 4971556Srgrimes (void)fprintf(stderr, "remove %s? ", path); 4981556Srgrimes else { 4991556Srgrimes /* 5001556Srgrimes * If it's not a symbolic link and it's unwritable and we're 501249949Seadler * talking to a terminal, ask. Symbolic links are excluded 5021556Srgrimes * because their permissions are meaningless. Check stdin_ok 5031556Srgrimes * first because we may not have stat'ed the file. 5041556Srgrimes */ 505150729Sdougb if (!stdin_ok || S_ISLNK(sp->st_mode) || 50620421Ssteve (!access(name, W_OK) && 5077798Sache !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 50820421Ssteve (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) 5091556Srgrimes return (1); 5101556Srgrimes strmode(sp->st_mode, modep); 51161749Sjoe if ((flagsp = fflagstostr(sp->st_flags)) == NULL) 51299744Sdillon err(1, "fflagstostr"); 513150729Sdougb if (Pflag) 514150729Sdougb errx(1, 515150729Sdougb "%s: -P was specified, but file is not writable", 516150729Sdougb path); 51761749Sjoe (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", 5181556Srgrimes modep + 1, modep[9] == ' ' ? "" : " ", 5191556Srgrimes user_from_uid(sp->st_uid, 0), 5207798Sache group_from_gid(sp->st_gid, 0), 521136112Sdes *flagsp ? flagsp : "", *flagsp ? " " : "", 5227798Sache path); 52361749Sjoe free(flagsp); 5241556Srgrimes } 5251556Srgrimes (void)fflush(stderr); 5261556Srgrimes 5271556Srgrimes first = ch = getchar(); 5281556Srgrimes while (ch != '\n' && ch != EOF) 5291556Srgrimes ch = getchar(); 53014409Swosch return (first == 'y' || first == 'Y'); 5311556Srgrimes} 5321556Srgrimes 533136113Sdes#define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0') 534249949Seadlerstatic void 535136113Sdescheckslash(char **argv) 536136113Sdes{ 537136113Sdes char **t, **u; 538136113Sdes int complained; 539136113Sdes 540136113Sdes complained = 0; 541136113Sdes for (t = argv; *t;) { 542136113Sdes if (ISSLASH(*t)) { 543136113Sdes if (!complained++) 544136113Sdes warnx("\"/\" may not be removed"); 545136113Sdes eval = 1; 546136113Sdes for (u = t; u[0] != NULL; ++u) 547136113Sdes u[0] = u[1]; 548136113Sdes } else { 549136113Sdes ++t; 550136113Sdes } 551136113Sdes } 552136113Sdes} 553136113Sdes 554249949Seadlerstatic int 555137009Sdelphijcheck2(char **argv) 556137009Sdelphij{ 557137009Sdelphij struct stat st; 558137009Sdelphij int first; 559137009Sdelphij int ch; 560137009Sdelphij int fcount = 0; 561137009Sdelphij int dcount = 0; 562137009Sdelphij int i; 563137009Sdelphij const char *dname = NULL; 564137009Sdelphij 565137009Sdelphij for (i = 0; argv[i]; ++i) { 566137009Sdelphij if (lstat(argv[i], &st) == 0) { 567137009Sdelphij if (S_ISDIR(st.st_mode)) { 568137009Sdelphij ++dcount; 569137009Sdelphij dname = argv[i]; /* only used if 1 dir */ 570137009Sdelphij } else { 571137009Sdelphij ++fcount; 572137009Sdelphij } 573137009Sdelphij } 574137009Sdelphij } 575137009Sdelphij first = 0; 576137009Sdelphij while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') { 577137009Sdelphij if (dcount && rflag) { 578137009Sdelphij fprintf(stderr, "recursively remove"); 579137009Sdelphij if (dcount == 1) 580137009Sdelphij fprintf(stderr, " %s", dname); 581137009Sdelphij else 582137009Sdelphij fprintf(stderr, " %d dirs", dcount); 583137009Sdelphij if (fcount == 1) 584137009Sdelphij fprintf(stderr, " and 1 file"); 585137009Sdelphij else if (fcount > 1) 586137009Sdelphij fprintf(stderr, " and %d files", fcount); 587137009Sdelphij } else if (dcount + fcount > 3) { 588137009Sdelphij fprintf(stderr, "remove %d files", dcount + fcount); 589137009Sdelphij } else { 590137009Sdelphij return(1); 591137009Sdelphij } 592137009Sdelphij fprintf(stderr, "? "); 593137009Sdelphij fflush(stderr); 594137009Sdelphij 595137009Sdelphij first = ch = getchar(); 596137009Sdelphij while (ch != '\n' && ch != EOF) 597137009Sdelphij ch = getchar(); 598137009Sdelphij if (ch == EOF) 599137009Sdelphij break; 600137009Sdelphij } 601137009Sdelphij return (first == 'y' || first == 'Y'); 602137009Sdelphij} 603137009Sdelphij 6042927Sphk#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) 605249949Seadlerstatic void 60690110Simpcheckdot(char **argv) 6071556Srgrimes{ 6081556Srgrimes char *p, **save, **t; 6091556Srgrimes int complained; 6101556Srgrimes 6111556Srgrimes complained = 0; 6121556Srgrimes for (t = argv; *t;) { 6131556Srgrimes if ((p = strrchr(*t, '/')) != NULL) 6141556Srgrimes ++p; 6151556Srgrimes else 6161556Srgrimes p = *t; 6171556Srgrimes if (ISDOT(p)) { 6181556Srgrimes if (!complained++) 6191556Srgrimes warnx("\".\" and \"..\" may not be removed"); 6201556Srgrimes eval = 1; 62120421Ssteve for (save = t; (t[0] = t[1]) != NULL; ++t) 62220421Ssteve continue; 6231556Srgrimes t = save; 6241556Srgrimes } else 6251556Srgrimes ++t; 6261556Srgrimes } 6271556Srgrimes} 6281556Srgrimes 629249949Seadlerstatic void 63090110Simpusage(void) 6311556Srgrimes{ 63253819Smharo 63354895Ssheldonh (void)fprintf(stderr, "%s\n%s\n", 634249948Seadler "usage: rm [-f | -i] [-dIPRrvWx] file ...", 63554895Ssheldonh " unlink file"); 63650539Smharo exit(EX_USAGE); 6371556Srgrimes} 638191670Simp 639191670Simpstatic void 640191670Simpsiginfo(int sig __unused) 641191670Simp{ 642191670Simp 643191670Simp info = 1; 644191670Simp} 645