1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rm.c 4.25 (Berkeley) 12/08/90"; 16 #endif /* not lint */ 17 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 #include <sys/errno.h> 21 #include <fts.h> 22 #include <unistd.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <stdlib.h> 26 27 int dflag, fflag, iflag, retval, stdin_ok; 28 29 /* 30 * rm -- 31 * This rm is different from historic rm's, but is expected to match 32 * POSIX 1003.2 behavior. The most visible difference is that -f 33 * has two specific effects now, ignore non-existent files and force 34 * file removal. 35 */ 36 37 main(argc, argv) 38 int argc; 39 char **argv; 40 { 41 extern char *optarg; 42 extern int optind; 43 int ch, rflag; 44 45 rflag = 0; 46 while ((ch = getopt(argc, argv, "dfiRr")) != EOF) 47 switch(ch) { 48 case 'd': 49 dflag = 1; 50 break; 51 case 'f': 52 fflag = 1; 53 iflag = 0; 54 break; 55 case 'i': 56 fflag = 0; 57 iflag = 1; 58 break; 59 case 'R': 60 case 'r': 61 rflag = 1; 62 break; 63 case '?': 64 default: 65 usage(); 66 } 67 argc -= optind; 68 argv += optind; 69 70 if (argc < 1) 71 usage(); 72 73 checkdot(argv); 74 if (!*argv) 75 exit(retval); 76 77 stdin_ok = isatty(STDIN_FILENO); 78 79 if (rflag) 80 rmtree(argv); 81 else 82 rmfile(argv); 83 exit(retval); 84 } 85 86 rmtree(argv) 87 char **argv; 88 { 89 register FTS *fts; 90 register FTSENT *p; 91 register int needstat; 92 struct stat sb; 93 94 /* 95 * Remove a file hierarchy. If forcing removal (-f), or interactive 96 * (-i) or can't ask anyway (stdin_ok), don't stat the file. 97 */ 98 needstat = !fflag && !iflag && stdin_ok; 99 100 /* 101 * If the -i option is specified, the user can skip on the pre-order 102 * visit. The fts_number field flags skipped directories. 103 */ 104 #define SKIPPED 1 105 106 if (!(fts = fts_open(argv, 107 needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, 108 (int (*)())NULL))) { 109 (void)fprintf(stderr, "rm: %s.\n", strerror(errno)); 110 exit(1); 111 } 112 while (p = fts_read(fts)) { 113 switch(p->fts_info) { 114 case FTS_DC: 115 case FTS_DNR: 116 case FTS_DNX: 117 break; 118 case FTS_ERR: 119 error(p->fts_path, errno); 120 exit(1); 121 /* 122 * FTS_NS: assume that if can't stat the file, it can't be 123 * unlinked. If we need stat information, should never see 124 * FTS_NS (unless a file specified as an argument doesn't 125 * exist), since such files are in directories that will be 126 * returned FTS_DNX. 127 */ 128 case FTS_NS: 129 if (!needstat) 130 break; 131 if (!lstat(p->fts_accpath, &sb)) /* XXX */ 132 break; 133 if (!fflag || errno != ENOENT) 134 error(p->fts_path, errno); 135 continue; 136 /* Pre-order: give user chance to skip. */ 137 case FTS_D: 138 if (iflag && !check(p->fts_path, p->fts_accpath, 139 &p->fts_statb)) { 140 (void)fts_set(fts, p, FTS_SKIP); 141 p->fts_number = SKIPPED; 142 } 143 continue; 144 /* Post-order: see if user skipped. */ 145 case FTS_DP: 146 if (p->fts_number == SKIPPED) 147 continue; 148 break; 149 } 150 151 if (!fflag && 152 !check(p->fts_path, p->fts_accpath, &p->fts_statb)) 153 continue; 154 155 /* 156 * If we can't read or search the directory, may still be 157 * able to remove it. Don't print out the un{read,search}able 158 * message unless the remove fails. 159 */ 160 if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR || 161 p->fts_info == FTS_DNX) { 162 if (!rmdir(p->fts_accpath)) 163 continue; 164 if (errno == ENOENT) { 165 if (fflag) 166 continue; 167 } else if (p->fts_info != FTS_DP) 168 (void)fprintf(stderr, "rm: unable to %s %s.\n", 169 p->fts_info == FTS_DNR ? "read" : "search", 170 p->fts_path); 171 } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) 172 continue; 173 error(p->fts_path, errno); 174 } 175 } 176 177 rmfile(argv) 178 char **argv; 179 { 180 register int df; 181 register char *f; 182 struct stat sb; 183 184 df = dflag; 185 /* 186 * Remove a file. POSIX 1003.2 states that, by default, attempting 187 * to remove a directory is an error, so must always stat the file. 188 */ 189 while (f = *argv++) { 190 /* Assume if can't stat the file, can't unlink it. */ 191 if (lstat(f, &sb)) { 192 if (!fflag || errno != ENOENT) 193 error(f, errno); 194 continue; 195 } 196 if (S_ISDIR(sb.st_mode) && !df) { 197 (void)fprintf(stderr, "rm: %s: Is a directory.\n", f); 198 retval = 1; 199 continue; 200 } 201 if (!fflag && !check(f, f, &sb)) 202 continue; 203 if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) && 204 (!fflag || errno != ENOENT)) 205 error(f, errno); 206 } 207 } 208 209 check(path, name, sp) 210 char *path, *name; 211 struct stat *sp; 212 { 213 register int first, ch; 214 char modep[15], *user_from_uid(), *group_from_gid(); 215 216 /* Check -i first. */ 217 if (iflag) 218 (void)fprintf(stderr, "remove %s? ", path); 219 else { 220 /* 221 * If it's not a symbolic link and it's unwritable and we're 222 * talking to a terminal, ask. Symbolic links are excluded 223 * because the permissions are meaningless. 224 */ 225 if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK)) 226 return(1); 227 strmode(sp->st_mode, modep); 228 (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 229 modep + 1, modep[9] == ' ' ? "" : " ", 230 user_from_uid(sp->st_uid, 0), 231 group_from_gid(sp->st_gid, 0), path); 232 } 233 (void)fflush(stderr); 234 235 first = ch = getchar(); 236 while (ch != '\n' && ch != EOF) 237 ch = getchar(); 238 return(first == 'y'); 239 } 240 241 #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) 242 checkdot(argv) 243 char **argv; 244 { 245 register char *p, **t, **save; 246 int complained; 247 248 complained = 0; 249 for (t = argv; *t;) { 250 if (p = rindex(*t, '/')) 251 ++p; 252 else 253 p = *t; 254 if (ISDOT(p)) { 255 if (!complained++) 256 (void)fprintf(stderr, 257 "rm: \".\" and \"..\" may not be removed.\n"); 258 retval = 1; 259 for (save = t; t[0] = t[1]; ++t); 260 t = save; 261 } else 262 ++t; 263 } 264 } 265 266 error(name, val) 267 char *name; 268 int val; 269 { 270 (void)fprintf(stderr, "rm: %s: %s.\n", name, strerror(val)); 271 retval = 1; 272 } 273 274 usage() 275 { 276 (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); 277 exit(1); 278 } 279