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