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*59515Sbostic static char sccsid[] = "@(#)rm.c 5.1 (Berkeley) 04/29/93"; 1645769Sbostic #endif /* not lint */ 1745769Sbostic 1845769Sbostic #include <sys/types.h> 1945769Sbostic #include <sys/stat.h> 2045769Sbostic #include <sys/errno.h> 21*59515Sbostic 22*59515Sbostic #include <err.h> 2345769Sbostic #include <fts.h> 241083Sbill #include <stdio.h> 25*59515Sbostic #include <stdlib.h> 2645769Sbostic #include <string.h> 27*59515Sbostic #include <unistd.h> 281083Sbill 29*59515Sbostic int dflag, fflag, iflag, eval, stdin_ok; 301083Sbill 31*59515Sbostic int check __P((char *, char *, struct stat *)); 32*59515Sbostic void checkdot __P((char **)); 33*59515Sbostic void rmfile __P((char **)); 34*59515Sbostic void rmtree __P((char **)); 35*59515Sbostic void usage __P((void)); 36*59515Sbostic 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 */ 44*59515Sbostic int 451083Sbill main(argc, argv) 4634044Sbostic int argc; 47*59515Sbostic char *argv[]; 481083Sbill { 4934044Sbostic extern int optind; 5045769Sbostic int ch, rflag; 511083Sbill 5245769Sbostic rflag = 0; 5345769Sbostic while ((ch = getopt(argc, argv, "dfiRr")) != EOF) 5445769Sbostic switch(ch) { 5545769Sbostic case 'd': 5645769Sbostic dflag = 1; 5745769Sbostic break; 5834044Sbostic case 'f': 5945769Sbostic fflag = 1; 6045769Sbostic iflag = 0; 6118277Sralph break; 6234044Sbostic case 'i': 6345769Sbostic fflag = 0; 6445769Sbostic iflag = 1; 6534044Sbostic break; 6634044Sbostic case 'R': 6747195Sbostic case 'r': /* compatibility */ 6845769Sbostic rflag = 1; 6934044Sbostic break; 7034044Sbostic case '?': 7134044Sbostic default: 7234044Sbostic usage(); 7334044Sbostic } 7445769Sbostic argc -= optind; 7545769Sbostic argv += optind; 761932Sroot 7745769Sbostic if (argc < 1) 7834044Sbostic usage(); 7945769Sbostic 8045769Sbostic checkdot(argv); 8145769Sbostic if (!*argv) 82*59515Sbostic exit (eval); 8345769Sbostic 8445769Sbostic stdin_ok = isatty(STDIN_FILENO); 8545769Sbostic 8645769Sbostic if (rflag) 8745769Sbostic rmtree(argv); 8845769Sbostic else 8945769Sbostic rmfile(argv); 90*59515Sbostic exit (eval); 911083Sbill } 921083Sbill 93*59515Sbostic void 9445769Sbostic rmtree(argv) 9545769Sbostic char **argv; 961083Sbill { 9745769Sbostic register FTS *fts; 9845769Sbostic register FTSENT *p; 9945769Sbostic register int needstat; 1001083Sbill 10145769Sbostic /* 10245769Sbostic * Remove a file hierarchy. If forcing removal (-f), or interactive 10345769Sbostic * (-i) or can't ask anyway (stdin_ok), don't stat the file. 10445769Sbostic */ 10545769Sbostic needstat = !fflag && !iflag && stdin_ok; 10645769Sbostic 10745769Sbostic /* 10845769Sbostic * If the -i option is specified, the user can skip on the pre-order 10945769Sbostic * visit. The fts_number field flags skipped directories. 11045769Sbostic */ 11145769Sbostic #define SKIPPED 1 11245769Sbostic 11345769Sbostic if (!(fts = fts_open(argv, 11445769Sbostic needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, 115*59515Sbostic (int (*)())NULL))) 116*59515Sbostic err(1, NULL); 11745769Sbostic while (p = fts_read(fts)) { 118*59515Sbostic switch (p->fts_info) { 11945769Sbostic case FTS_DNR: 12045769Sbostic case FTS_ERR: 121*59515Sbostic err(1, "%s", p->fts_path); 122*59515Sbostic 12345769Sbostic case FTS_NS: 124*59515Sbostic /* 125*59515Sbostic * FTS_NS: assume that if can't stat the file, it 126*59515Sbostic * can't be unlinked. 127*59515Sbostic */ 12845769Sbostic if (!needstat) 12945769Sbostic break; 130*59515Sbostic if (!fflag || errno != ENOENT) { 131*59515Sbostic warn("%s", p->fts_path); 132*59515Sbostic eval = 1; 133*59515Sbostic } 13445769Sbostic continue; 135*59515Sbostic 13645769Sbostic case FTS_D: 137*59515Sbostic /* Pre-order: give user chance to skip. */ 13845769Sbostic if (iflag && !check(p->fts_path, p->fts_accpath, 13952238Sbostic p->fts_statp)) { 14045769Sbostic (void)fts_set(fts, p, FTS_SKIP); 14145769Sbostic p->fts_number = SKIPPED; 14218327Sralph } 14345769Sbostic continue; 144*59515Sbostic 14545769Sbostic case FTS_DP: 146*59515Sbostic /* Post-order: see if user skipped. */ 14745769Sbostic if (p->fts_number == SKIPPED) 14845769Sbostic continue; 14945769Sbostic break; 15018277Sralph } 15145769Sbostic 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) 168*59515Sbostic warnx("%s: unable to read", p->fts_path); 16945769Sbostic } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 17047195Sbostic continue; 171*59515Sbostic warn("%s", p->fts_path); 172*59515Sbostic eval = 1; 1731083Sbill } 17445769Sbostic } 1751083Sbill 176*59515Sbostic void 17745769Sbostic rmfile(argv) 17845769Sbostic char **argv; 17945769Sbostic { 18045769Sbostic register int df; 18145769Sbostic register 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)) { 192*59515Sbostic if (!fflag || errno != ENOENT) { 193*59515Sbostic warn("%s", f); 194*59515Sbostic eval = 1; 195*59515Sbostic } 19645769Sbostic continue; 1971083Sbill } 19845769Sbostic if (S_ISDIR(sb.st_mode) && !df) { 199*59515Sbostic warnx("%s: is a directory", f); 200*59515Sbostic 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)) && 206*59515Sbostic (!fflag || errno != ENOENT)) { 207*59515Sbostic warn("%s", f); 208*59515Sbostic eval = 1; 209*59515Sbostic } 2101083Sbill } 2111083Sbill } 2121083Sbill 213*59515Sbostic int 21445769Sbostic check(path, name, sp) 21545769Sbostic char *path, *name; 21645769Sbostic struct stat *sp; 2171083Sbill { 21845769Sbostic register int first, ch; 219*59515Sbostic 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 22847195Sbostic * because their permissions are meaningless. 22945769Sbostic */ 23045769Sbostic if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK)) 231*59515Sbostic return (1); 23245769Sbostic strmode(sp->st_mode, modep); 23345769Sbostic (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 23445769Sbostic modep + 1, modep[9] == ' ' ? "" : " ", 23545769Sbostic user_from_uid(sp->st_uid, 0), 23645769Sbostic group_from_gid(sp->st_gid, 0), path); 23745769Sbostic } 23845769Sbostic (void)fflush(stderr); 23945769Sbostic 24045769Sbostic first = ch = getchar(); 24145769Sbostic while (ch != '\n' && ch != EOF) 24245769Sbostic ch = getchar(); 243*59515Sbostic return (first == 'y'); 2441083Sbill } 24518277Sralph 24645769Sbostic #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 247*59515Sbostic void 24845769Sbostic checkdot(argv) 24945769Sbostic char **argv; 25018277Sralph { 25145769Sbostic register char *p, **t, **save; 25245769Sbostic int complained; 25318277Sralph 25445769Sbostic complained = 0; 25545769Sbostic for (t = argv; *t;) { 256*59515Sbostic if (p = strrchr(*t, '/')) 25745769Sbostic ++p; 25845769Sbostic else 25945769Sbostic p = *t; 26045769Sbostic if (ISDOT(p)) { 26145769Sbostic if (!complained++) 262*59515Sbostic warnx("\".\" and \"..\" may not be removed"); 263*59515Sbostic eval = 1; 26445769Sbostic for (save = t; t[0] = t[1]; ++t); 26545769Sbostic t = save; 26645769Sbostic } else 26745769Sbostic ++t; 26845769Sbostic } 26918277Sralph } 27034044Sbostic 271*59515Sbostic void 27234044Sbostic usage() 27334044Sbostic { 27445769Sbostic (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 27534044Sbostic exit(1); 27634044Sbostic } 277