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