xref: /original-bsd/bin/rm/rm.c (revision e718337e)
1 static char *sccsid = "@(#)rm.c	4.24 (Berkeley) 11/05/90";
2 
3 /*
4  * rm - for ReMoving files, directories & trees.
5  */
6 
7 #include <stdio.h>
8 #include <sys/param.h>
9 #include <sys/errno.h>
10 #include <sys/stat.h>
11 #include <sys/dir.h>
12 #include <sys/file.h>
13 
14 int	fflg;		/* -f force - supress error messages */
15 int	iflg;		/* -i interrogate user on each file */
16 int	rflg;		/* -r recurse */
17 
18 int	errcode;	/* true if errors occured */
19 
20 char	*strcpy();
21 
22 main(argc, argv)
23 	int argc;
24 	char **argv;
25 {
26 	extern int optind;
27 	int ch;
28 
29 	fflg = !isatty(0);
30 	while ((ch = getopt(argc, argv, "-Rfir")) != EOF)
31 		switch((char)ch) {
32 		case '-':
33 			goto endarg;
34 		case 'f':
35 			fflg++;
36 			break;
37 		case 'i':
38 			iflg++;
39 			break;
40 		case 'R':
41 		case 'r':
42 			rflg++;
43 			break;
44 		case '?':
45 		default:
46 			usage();
47 		}
48 endarg:	argv += optind;
49 
50 	if (!*argv) {
51 		if (fflg)
52 			exit(0);
53 		usage();
54 	}
55 	do {
56 		(void)rm(*argv, 0);
57 	} while (*++argv);
58 	exit(errcode != 0);
59 }
60 
61 char	*path;		/* pointer to malloc'ed buffer for path */
62 char	*pathp;		/* current pointer to end of path */
63 int	pathsz;		/* size of path */
64 
65 #define	isdot(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
66 /*
67  * Return TRUE if sucessful. Recursive with -r (rflg)
68  */
69 rm(arg, level)
70 	char arg[];
71 {
72 	int ok;				/* true if recursive rm succeeded */
73 	struct stat buf;		/* for finding out what a file is */
74 	struct direct *dp;		/* for reading a directory */
75 	DIR *dirp;			/* for reading a directory */
76 	char prevname[MAXNAMLEN + 1];	/* previous name for -r */
77 	char *cp, *rindex();
78 
79 	cp = rindex(arg, '/');
80 	if (cp == NULL)
81 		cp = arg;
82 	else
83 		++cp;
84 	if (isdot(cp)) {
85 		fprintf(stderr, "rm: cannot remove `.' or `..'\n");
86 		return (0);
87 	}
88 	if (lstat(arg, &buf)) {
89 		if (!fflg) {
90 			fprintf(stderr, "rm: %s: %s\n", arg, strerror(errno));
91 			errcode++;
92 		}
93 		return (0);		/* error */
94 	}
95 	if ((buf.st_mode&S_IFMT) == S_IFDIR) {
96 		if (!rflg) {
97 			if (!fflg) {
98 				fprintf(stderr, "rm: %s directory\n", arg);
99 				errcode++;
100 			}
101 			return (0);
102 		}
103 		if (iflg && level != 0) {
104 			printf("rm: remove directory %s? ", arg);
105 			if (!yes())
106 				return (0);	/* didn't remove everything */
107 		}
108 		if (access(arg, R_OK|W_OK|X_OK) != 0) {
109 			if (rmdir(arg) == 0)
110 				return (1);	/* salvaged: removed empty dir */
111 			if (!fflg) {
112 				fprintf(stderr, "rm: %s not removed: %s\n", arg,
113 				    strerror(errno));
114 				errcode++;
115 			}
116 			return (0);		/* error */
117 		}
118 		if ((dirp = opendir(arg)) == NULL) {
119 			if (!fflg) {
120 				fprintf(stderr, "rm: cannot read %s: %s\n",
121 				    arg, strerror(errno));
122 				errcode++;
123 			}
124 			return (0);
125 		}
126 		if (level == 0)
127 			append(arg);
128 		prevname[0] = '\0';
129 		while ((dp = readdir(dirp)) != NULL) {
130 			if (isdot(dp->d_name)) {
131 				strcpy(prevname, dp->d_name);
132 				continue;
133 			}
134 			append(dp->d_name);
135 			closedir(dirp);
136 			ok = rm(path, level + 1);
137 			for (cp = pathp; *--cp != '/' && cp > path; )
138 				;
139 			pathp = cp;
140 			*cp++ = '\0';
141 			if ((dirp = opendir(arg)) == NULL) {
142 				if (!fflg) {
143 					fprintf(stderr,
144 					    "rm: cannot reopen %s?: %s\n",
145 					    arg, strerror(errno));
146 					errcode++;
147 				}
148 				break;
149 			}
150 			/* pick up where we left off */
151 			if (prevname[0] != '\0') {
152 				while ((dp = readdir(dirp)) != NULL &&
153 				    strcmp(prevname, dp->d_name) != 0)
154 					;
155 			}
156 			/* skip the one we just failed to delete */
157 			if (!ok) {
158 				dp = readdir(dirp);
159 				if (dp != NULL && strcmp(cp, dp->d_name)) {
160 					fprintf(stderr,
161 			"rm: internal synchronization error: %s, %s, %s\n",
162 						arg, cp, dp->d_name);
163 				}
164 				strcpy(prevname, dp->d_name);
165 			}
166 		}
167 		closedir(dirp);
168 		if (level == 0) {
169 			pathp = path;
170 			*pathp = '\0';
171 		}
172 		if (iflg) {
173 			printf("rm: remove %s? ", arg);
174 			if (!yes())
175 				return (0);
176 		}
177 		if (rmdir(arg) < 0) {
178 			if (!fflg || iflg) {
179 				fprintf(stderr, "rm: %s not removed: %s\n", arg,
180 				    strerror(errno));
181 				errcode++;
182 			}
183 			return (0);
184 		}
185 		return (1);
186 	}
187 
188 	if (!fflg) {
189 		if ((buf.st_mode&S_IFMT) != S_IFLNK && access(arg, W_OK) < 0) {
190 			printf("rm: override protection %o for %s? ",
191 				buf.st_mode&0777, arg);
192 			if (!yes())
193 				return (0);
194 			goto rm;
195 		}
196 	}
197 	if (iflg) {
198 		printf("rm: remove %s? ", arg);
199 		if (!yes())
200 			return (0);
201 	}
202 rm:	if (unlink(arg) < 0) {
203 		if (!fflg || iflg) {
204 			fprintf(stderr, "rm: %s: %s\n", arg, strerror(errno));
205 			errcode++;
206 		}
207 		return (0);
208 	}
209 	return (1);
210 }
211 
212 /*
213  * Get a yes/no answer from the user.
214  */
215 yes()
216 {
217 	int i, b;
218 
219 	i = b = getchar();
220 	while (b != '\n' && b != EOF)
221 		b = getchar();
222 	return (i == 'y');
223 }
224 
225 /*
226  * Append 'name' to 'path'.
227  */
228 append(name)
229 	char *name;
230 {
231 	register int n;
232 	char *malloc();
233 
234 	n = strlen(name);
235 	if (path == NULL) {
236 		pathsz = MAXNAMLEN + MAXPATHLEN + 2;
237 		if ((path = malloc((u_int)pathsz)) == NULL) {
238 			fprintf(stderr, "rm: ran out of memory\n");
239 			exit(1);
240 		}
241 		pathp = path;
242 	} else if (pathp + n + 2 > path + pathsz) {
243 		fprintf(stderr, "rm: path name too long: %s\n", path);
244 		exit(1);
245 	} else if (pathp != path && pathp[-1] != '/')
246 		*pathp++ = '/';
247 	strcpy(pathp, name);
248 	pathp += n;
249 }
250 
251 usage()
252 {
253 	fputs("usage: rm [-rif] file ...\n", stderr);
254 	exit(1);
255 }
256