145769Sbostic /*- 245769Sbostic * Copyright (c) 1990 The Regents of the University of California. 345769Sbostic * All rights reserved. 445769Sbostic * 545769Sbostic * %sccs.include.redist.c% 618277Sralph */ 718277Sralph 845769Sbostic #ifndef lint 945769Sbostic char copyright[] = 1045769Sbostic "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ 1145769Sbostic All rights reserved.\n"; 1245769Sbostic #endif /* not lint */ 1345769Sbostic 1445769Sbostic #ifndef lint 15*47195Sbostic static char sccsid[] = "@(#)rm.c 4.26 (Berkeley) 03/10/91"; 1645769Sbostic #endif /* not lint */ 1745769Sbostic 1845769Sbostic #include <sys/types.h> 1945769Sbostic #include <sys/stat.h> 2045769Sbostic #include <sys/errno.h> 2145769Sbostic #include <fts.h> 2245769Sbostic #include <unistd.h> 231083Sbill #include <stdio.h> 2445769Sbostic #include <string.h> 2545769Sbostic #include <stdlib.h> 261083Sbill 2745769Sbostic int dflag, fflag, iflag, retval, stdin_ok; 281083Sbill 2945769Sbostic /* 3045769Sbostic * rm -- 3145769Sbostic * This rm is different from historic rm's, but is expected to match 3245769Sbostic * POSIX 1003.2 behavior. The most visible difference is that -f 3345769Sbostic * has two specific effects now, ignore non-existent files and force 3445769Sbostic * file removal. 3545769Sbostic */ 3618277Sralph 371083Sbill main(argc, argv) 3834044Sbostic int argc; 3934044Sbostic char **argv; 401083Sbill { 4145769Sbostic extern char *optarg; 4234044Sbostic extern int optind; 4345769Sbostic int ch, rflag; 441083Sbill 4545769Sbostic rflag = 0; 4645769Sbostic while ((ch = getopt(argc, argv, "dfiRr")) != EOF) 4745769Sbostic switch(ch) { 4845769Sbostic case 'd': 4945769Sbostic dflag = 1; 5045769Sbostic break; 5134044Sbostic case 'f': 5245769Sbostic fflag = 1; 5345769Sbostic iflag = 0; 5418277Sralph break; 5534044Sbostic case 'i': 5645769Sbostic fflag = 0; 5745769Sbostic iflag = 1; 5834044Sbostic break; 5934044Sbostic case 'R': 60*47195Sbostic case 'r': /* compatibility */ 6145769Sbostic rflag = 1; 6234044Sbostic break; 6334044Sbostic case '?': 6434044Sbostic default: 6534044Sbostic usage(); 6634044Sbostic } 6745769Sbostic argc -= optind; 6845769Sbostic argv += optind; 691932Sroot 7045769Sbostic if (argc < 1) 7134044Sbostic usage(); 7245769Sbostic 7345769Sbostic checkdot(argv); 7445769Sbostic if (!*argv) 7545769Sbostic exit(retval); 7645769Sbostic 7745769Sbostic stdin_ok = isatty(STDIN_FILENO); 7845769Sbostic 7945769Sbostic if (rflag) 8045769Sbostic rmtree(argv); 8145769Sbostic else 8245769Sbostic rmfile(argv); 8345769Sbostic exit(retval); 841083Sbill } 851083Sbill 8645769Sbostic rmtree(argv) 8745769Sbostic char **argv; 881083Sbill { 8945769Sbostic register FTS *fts; 9045769Sbostic register FTSENT *p; 9145769Sbostic register int needstat; 9245769Sbostic struct stat sb; 931083Sbill 9445769Sbostic /* 9545769Sbostic * Remove a file hierarchy. If forcing removal (-f), or interactive 9645769Sbostic * (-i) or can't ask anyway (stdin_ok), don't stat the file. 9745769Sbostic */ 9845769Sbostic needstat = !fflag && !iflag && stdin_ok; 9945769Sbostic 10045769Sbostic /* 10145769Sbostic * If the -i option is specified, the user can skip on the pre-order 10245769Sbostic * visit. The fts_number field flags skipped directories. 10345769Sbostic */ 10445769Sbostic #define SKIPPED 1 10545769Sbostic 10645769Sbostic if (!(fts = fts_open(argv, 10745769Sbostic needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, 10845769Sbostic (int (*)())NULL))) { 10945769Sbostic (void)fprintf(stderr, "rm: %s.\n", strerror(errno)); 11045769Sbostic exit(1); 1111083Sbill } 11245769Sbostic while (p = fts_read(fts)) { 11345769Sbostic switch(p->fts_info) { 11445769Sbostic case FTS_DNR: 11545769Sbostic case FTS_ERR: 11645769Sbostic error(p->fts_path, errno); 11745769Sbostic exit(1); 11845769Sbostic /* 11945769Sbostic * FTS_NS: assume that if can't stat the file, it can't be 120*47195Sbostic * unlinked. 12145769Sbostic */ 12245769Sbostic case FTS_NS: 12345769Sbostic if (!needstat) 12445769Sbostic break; 12545769Sbostic if (!fflag || errno != ENOENT) 12645769Sbostic error(p->fts_path, errno); 12745769Sbostic continue; 12845769Sbostic /* Pre-order: give user chance to skip. */ 12945769Sbostic case FTS_D: 13045769Sbostic if (iflag && !check(p->fts_path, p->fts_accpath, 13145769Sbostic &p->fts_statb)) { 13245769Sbostic (void)fts_set(fts, p, FTS_SKIP); 13345769Sbostic p->fts_number = SKIPPED; 13418327Sralph } 13545769Sbostic continue; 13645769Sbostic /* Post-order: see if user skipped. */ 13745769Sbostic case FTS_DP: 13845769Sbostic if (p->fts_number == SKIPPED) 13945769Sbostic continue; 14045769Sbostic break; 14118277Sralph } 14245769Sbostic 14345769Sbostic if (!fflag && 14445769Sbostic !check(p->fts_path, p->fts_accpath, &p->fts_statb)) 14545769Sbostic continue; 14645769Sbostic 14745769Sbostic /* 14845769Sbostic * If we can't read or search the directory, may still be 14945769Sbostic * able to remove it. Don't print out the un{read,search}able 15045769Sbostic * message unless the remove fails. 15145769Sbostic */ 152*47195Sbostic if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { 15345769Sbostic if (!rmdir(p->fts_accpath)) 15418277Sralph continue; 15545769Sbostic if (errno == ENOENT) { 15645769Sbostic if (fflag) 15745769Sbostic continue; 15845769Sbostic } else if (p->fts_info != FTS_DP) 159*47195Sbostic (void)fprintf(stderr, 160*47195Sbostic "rm: unable to read %s.\n", p->fts_path); 16145769Sbostic } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 162*47195Sbostic continue; 16345769Sbostic error(p->fts_path, errno); 1641083Sbill } 16545769Sbostic } 1661083Sbill 16745769Sbostic rmfile(argv) 16845769Sbostic char **argv; 16945769Sbostic { 17045769Sbostic register int df; 17145769Sbostic register char *f; 17245769Sbostic struct stat sb; 17345769Sbostic 17445769Sbostic df = dflag; 17545769Sbostic /* 17645769Sbostic * Remove a file. POSIX 1003.2 states that, by default, attempting 17745769Sbostic * to remove a directory is an error, so must always stat the file. 17845769Sbostic */ 17945769Sbostic while (f = *argv++) { 18045769Sbostic /* Assume if can't stat the file, can't unlink it. */ 18145769Sbostic if (lstat(f, &sb)) { 18245769Sbostic if (!fflag || errno != ENOENT) 18345769Sbostic error(f, errno); 18445769Sbostic continue; 1851083Sbill } 18645769Sbostic if (S_ISDIR(sb.st_mode) && !df) { 187*47195Sbostic (void)fprintf(stderr, "rm: %s: is a directory\n", f); 18845769Sbostic retval = 1; 18945769Sbostic continue; 19018327Sralph } 19145769Sbostic if (!fflag && !check(f, f, &sb)) 19245769Sbostic continue; 19345769Sbostic if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) && 19445769Sbostic (!fflag || errno != ENOENT)) 19545769Sbostic error(f, errno); 1961083Sbill } 1971083Sbill } 1981083Sbill 19945769Sbostic check(path, name, sp) 20045769Sbostic char *path, *name; 20145769Sbostic struct stat *sp; 2021083Sbill { 20345769Sbostic register int first, ch; 20445769Sbostic char modep[15], *user_from_uid(), *group_from_gid(); 2051083Sbill 20645769Sbostic /* Check -i first. */ 20745769Sbostic if (iflag) 20845769Sbostic (void)fprintf(stderr, "remove %s? ", path); 20945769Sbostic else { 21045769Sbostic /* 21145769Sbostic * If it's not a symbolic link and it's unwritable and we're 21245769Sbostic * talking to a terminal, ask. Symbolic links are excluded 213*47195Sbostic * because their permissions are meaningless. 21445769Sbostic */ 21545769Sbostic if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK)) 21645769Sbostic return(1); 21745769Sbostic strmode(sp->st_mode, modep); 21845769Sbostic (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 21945769Sbostic modep + 1, modep[9] == ' ' ? "" : " ", 22045769Sbostic user_from_uid(sp->st_uid, 0), 22145769Sbostic group_from_gid(sp->st_gid, 0), path); 22245769Sbostic } 22345769Sbostic (void)fflush(stderr); 22445769Sbostic 22545769Sbostic first = ch = getchar(); 22645769Sbostic while (ch != '\n' && ch != EOF) 22745769Sbostic ch = getchar(); 22845769Sbostic return(first == 'y'); 2291083Sbill } 23018277Sralph 23145769Sbostic #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 23245769Sbostic checkdot(argv) 23345769Sbostic char **argv; 23418277Sralph { 23545769Sbostic register char *p, **t, **save; 23645769Sbostic int complained; 23718277Sralph 23845769Sbostic complained = 0; 23945769Sbostic for (t = argv; *t;) { 24045769Sbostic if (p = rindex(*t, '/')) 24145769Sbostic ++p; 24245769Sbostic else 24345769Sbostic p = *t; 24445769Sbostic if (ISDOT(p)) { 24545769Sbostic if (!complained++) 24645769Sbostic (void)fprintf(stderr, 24745769Sbostic "rm: \".\" and \"..\" may not be removed.\n"); 24845769Sbostic retval = 1; 24945769Sbostic for (save = t; t[0] = t[1]; ++t); 25045769Sbostic t = save; 25145769Sbostic } else 25245769Sbostic ++t; 25345769Sbostic } 25418277Sralph } 25534044Sbostic 25645769Sbostic error(name, val) 25745769Sbostic char *name; 25845769Sbostic int val; 25945769Sbostic { 26045769Sbostic (void)fprintf(stderr, "rm: %s: %s.\n", name, strerror(val)); 26145769Sbostic retval = 1; 26245769Sbostic } 26345769Sbostic 26434044Sbostic usage() 26534044Sbostic { 26645769Sbostic (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 26734044Sbostic exit(1); 26834044Sbostic } 269