145769Sbostic /*- 261008Sbostic * Copyright (c) 1990, 1993 361008Sbostic * The Regents of the University of California. All rights reserved. 445769Sbostic * 545769Sbostic * %sccs.include.redist.c% 618277Sralph */ 718277Sralph 845769Sbostic #ifndef lint 961008Sbostic static char copyright[] = 1061008Sbostic "@(#) Copyright (c) 1990, 1993\n\ 1161008Sbostic The Regents of the University of California. All rights reserved.\n"; 1245769Sbostic #endif /* not lint */ 1345769Sbostic 1445769Sbostic #ifndef lint 15*66561Spendry static char sccsid[] = "@(#)rm.c 8.3 (Berkeley) 04/01/94"; 1645769Sbostic #endif /* not lint */ 1745769Sbostic 1845769Sbostic #include <sys/types.h> 1945769Sbostic #include <sys/stat.h> 2059515Sbostic 2159515Sbostic #include <err.h> 22*66561Spendry #include <errno.h> 2345769Sbostic #include <fts.h> 241083Sbill #include <stdio.h> 2559515Sbostic #include <stdlib.h> 2645769Sbostic #include <string.h> 2759515Sbostic #include <unistd.h> 281083Sbill 2959515Sbostic int dflag, fflag, iflag, eval, stdin_ok; 301083Sbill 3159515Sbostic int check __P((char *, char *, struct stat *)); 3259515Sbostic void checkdot __P((char **)); 3359515Sbostic void rmfile __P((char **)); 3459515Sbostic void rmtree __P((char **)); 3559515Sbostic void usage __P((void)); 3659515Sbostic 3745769Sbostic /* 3845769Sbostic * rm -- 3945769Sbostic * This rm is different from historic rm's, but is expected to match 4045769Sbostic * POSIX 1003.2 behavior. The most visible difference is that -f 4145769Sbostic * has two specific effects now, ignore non-existent files and force 4245769Sbostic * file removal. 4345769Sbostic */ 4459515Sbostic int 451083Sbill main(argc, argv) 4634044Sbostic int argc; 4759515Sbostic char *argv[]; 481083Sbill { 4945769Sbostic int ch, rflag; 501083Sbill 5145769Sbostic rflag = 0; 5245769Sbostic while ((ch = getopt(argc, argv, "dfiRr")) != EOF) 5345769Sbostic switch(ch) { 5445769Sbostic case 'd': 5545769Sbostic dflag = 1; 5645769Sbostic break; 5734044Sbostic case 'f': 5845769Sbostic fflag = 1; 5945769Sbostic iflag = 0; 6018277Sralph break; 6134044Sbostic case 'i': 6245769Sbostic fflag = 0; 6345769Sbostic iflag = 1; 6434044Sbostic break; 6534044Sbostic case 'R': 6647195Sbostic case 'r': /* compatibility */ 6745769Sbostic rflag = 1; 6834044Sbostic break; 6934044Sbostic case '?': 7034044Sbostic default: 7134044Sbostic usage(); 7234044Sbostic } 7345769Sbostic argc -= optind; 7445769Sbostic argv += optind; 751932Sroot 7645769Sbostic if (argc < 1) 7734044Sbostic usage(); 7845769Sbostic 7945769Sbostic checkdot(argv); 8045769Sbostic if (!*argv) 8159515Sbostic exit (eval); 8245769Sbostic 8345769Sbostic stdin_ok = isatty(STDIN_FILENO); 8445769Sbostic 8545769Sbostic if (rflag) 8645769Sbostic rmtree(argv); 8745769Sbostic else 8845769Sbostic rmfile(argv); 8959515Sbostic exit (eval); 901083Sbill } 911083Sbill 9259515Sbostic void 9345769Sbostic rmtree(argv) 9445769Sbostic char **argv; 951083Sbill { 96*66561Spendry FTS *fts; 97*66561Spendry FTSENT *p; 98*66561Spendry int needstat; 991083Sbill 10045769Sbostic /* 10145769Sbostic * Remove a file hierarchy. If forcing removal (-f), or interactive 10245769Sbostic * (-i) or can't ask anyway (stdin_ok), don't stat the file. 10345769Sbostic */ 10445769Sbostic needstat = !fflag && !iflag && stdin_ok; 10545769Sbostic 10645769Sbostic /* 10745769Sbostic * If the -i option is specified, the user can skip on the pre-order 10845769Sbostic * visit. The fts_number field flags skipped directories. 10945769Sbostic */ 11045769Sbostic #define SKIPPED 1 11145769Sbostic 11245769Sbostic if (!(fts = fts_open(argv, 11345769Sbostic needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, 11459515Sbostic (int (*)())NULL))) 11559515Sbostic err(1, NULL); 11645769Sbostic while (p = fts_read(fts)) { 11759515Sbostic switch (p->fts_info) { 11845769Sbostic case FTS_DNR: 11961007Sbostic if (!fflag || errno != ENOENT) { 12061007Sbostic warn("%s", p->fts_path); 12161007Sbostic eval = 1; 12261007Sbostic } 12361007Sbostic continue; 12445769Sbostic case FTS_ERR: 12559515Sbostic err(1, "%s", p->fts_path); 12645769Sbostic case FTS_NS: 12759515Sbostic /* 12859515Sbostic * FTS_NS: assume that if can't stat the file, it 12959515Sbostic * can't be unlinked. 13059515Sbostic */ 13145769Sbostic if (!needstat) 13245769Sbostic break; 13359515Sbostic if (!fflag || errno != ENOENT) { 13459515Sbostic warn("%s", p->fts_path); 13559515Sbostic eval = 1; 13659515Sbostic } 13745769Sbostic continue; 13845769Sbostic case FTS_D: 13959515Sbostic /* Pre-order: give user chance to skip. */ 14045769Sbostic if (iflag && !check(p->fts_path, p->fts_accpath, 14152238Sbostic p->fts_statp)) { 14245769Sbostic (void)fts_set(fts, p, FTS_SKIP); 14345769Sbostic p->fts_number = SKIPPED; 14418327Sralph } 14545769Sbostic continue; 14645769Sbostic case FTS_DP: 14759515Sbostic /* Post-order: see if user skipped. */ 14845769Sbostic if (p->fts_number == SKIPPED) 14945769Sbostic continue; 15045769Sbostic break; 15118277Sralph } 15245769Sbostic if (!fflag && 15352238Sbostic !check(p->fts_path, p->fts_accpath, p->fts_statp)) 15445769Sbostic continue; 15545769Sbostic 15645769Sbostic /* 15745769Sbostic * If we can't read or search the directory, may still be 15845769Sbostic * able to remove it. Don't print out the un{read,search}able 15945769Sbostic * message unless the remove fails. 16045769Sbostic */ 16147195Sbostic if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { 16245769Sbostic if (!rmdir(p->fts_accpath)) 16318277Sralph continue; 16445769Sbostic if (errno == ENOENT) { 16545769Sbostic if (fflag) 16645769Sbostic continue; 16745769Sbostic } else if (p->fts_info != FTS_DP) 16859515Sbostic warnx("%s: unable to read", p->fts_path); 16945769Sbostic } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 17047195Sbostic continue; 17159515Sbostic warn("%s", p->fts_path); 17259515Sbostic eval = 1; 1731083Sbill } 17445769Sbostic } 1751083Sbill 17659515Sbostic void 17745769Sbostic rmfile(argv) 17845769Sbostic char **argv; 17945769Sbostic { 180*66561Spendry int df; 181*66561Spendry char *f; 18245769Sbostic struct stat sb; 18345769Sbostic 18445769Sbostic df = dflag; 18545769Sbostic /* 18645769Sbostic * Remove a file. POSIX 1003.2 states that, by default, attempting 18745769Sbostic * to remove a directory is an error, so must always stat the file. 18845769Sbostic */ 18945769Sbostic while (f = *argv++) { 19045769Sbostic /* Assume if can't stat the file, can't unlink it. */ 19145769Sbostic if (lstat(f, &sb)) { 19259515Sbostic if (!fflag || errno != ENOENT) { 19359515Sbostic warn("%s", f); 19459515Sbostic eval = 1; 19559515Sbostic } 19645769Sbostic continue; 1971083Sbill } 19845769Sbostic if (S_ISDIR(sb.st_mode) && !df) { 19959515Sbostic warnx("%s: is a directory", f); 20059515Sbostic eval = 1; 20145769Sbostic continue; 20218327Sralph } 20345769Sbostic if (!fflag && !check(f, f, &sb)) 20445769Sbostic continue; 20545769Sbostic if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) && 20659515Sbostic (!fflag || errno != ENOENT)) { 20759515Sbostic warn("%s", f); 20859515Sbostic eval = 1; 20959515Sbostic } 2101083Sbill } 2111083Sbill } 2121083Sbill 21359515Sbostic int 21445769Sbostic check(path, name, sp) 21545769Sbostic char *path, *name; 21645769Sbostic struct stat *sp; 2171083Sbill { 218*66561Spendry int ch, first; 21959515Sbostic char modep[15]; 2201083Sbill 22145769Sbostic /* Check -i first. */ 22245769Sbostic if (iflag) 22345769Sbostic (void)fprintf(stderr, "remove %s? ", path); 22445769Sbostic else { 22545769Sbostic /* 22645769Sbostic * If it's not a symbolic link and it's unwritable and we're 22745769Sbostic * talking to a terminal, ask. Symbolic links are excluded 22865344Sbostic * because their permissions are meaningless. Check stdin_ok 22965344Sbostic * first because we may not have stat'ed the file. 23045769Sbostic */ 23165344Sbostic if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) 23259515Sbostic return (1); 23345769Sbostic strmode(sp->st_mode, modep); 23445769Sbostic (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 23545769Sbostic modep + 1, modep[9] == ' ' ? "" : " ", 23645769Sbostic user_from_uid(sp->st_uid, 0), 23745769Sbostic group_from_gid(sp->st_gid, 0), path); 23845769Sbostic } 23945769Sbostic (void)fflush(stderr); 24045769Sbostic 24145769Sbostic first = ch = getchar(); 24245769Sbostic while (ch != '\n' && ch != EOF) 24345769Sbostic ch = getchar(); 24459515Sbostic return (first == 'y'); 2451083Sbill } 24618277Sralph 24745769Sbostic #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 24859515Sbostic void 24945769Sbostic checkdot(argv) 25045769Sbostic char **argv; 25118277Sralph { 252*66561Spendry char *p, **save, **t; 25345769Sbostic int complained; 25418277Sralph 25545769Sbostic complained = 0; 25645769Sbostic for (t = argv; *t;) { 25759515Sbostic if (p = strrchr(*t, '/')) 25845769Sbostic ++p; 25945769Sbostic else 26045769Sbostic p = *t; 26145769Sbostic if (ISDOT(p)) { 26245769Sbostic if (!complained++) 26359515Sbostic warnx("\".\" and \"..\" may not be removed"); 26459515Sbostic eval = 1; 26545769Sbostic for (save = t; t[0] = t[1]; ++t); 26645769Sbostic t = save; 26745769Sbostic } else 26845769Sbostic ++t; 26945769Sbostic } 27018277Sralph } 27134044Sbostic 27259515Sbostic void 27334044Sbostic usage() 27434044Sbostic { 275*66561Spendry 27645769Sbostic (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 27734044Sbostic exit(1); 27834044Sbostic } 279