xref: /original-bsd/bin/rm/rm.c (revision bdd86a84)
1 static char *sccsid = "@(#)rm.c	4.22 (Berkeley) 04/21/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(), *malloc();
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;
77 
78 	if (isdot(arg)) {
79 		fprintf(stderr, "rm: cannot remove `.' or `..'\n");
80 		return (0);
81 	}
82 	if (lstat(arg, &buf)) {
83 		if (!fflg) {
84 			fprintf(stderr, "rm: %s nonexistent\n", arg);
85 			errcode++;
86 		}
87 		return (0);		/* error */
88 	}
89 	if ((buf.st_mode&S_IFMT) == S_IFDIR) {
90 		if (!rflg) {
91 			if (!fflg) {
92 				fprintf(stderr, "rm: %s directory\n", arg);
93 				errcode++;
94 			}
95 			return (0);
96 		}
97 		if (iflg && level != 0) {
98 			printf("rm: remove directory %s? ", arg);
99 			if (!yes())
100 				return (0);	/* didn't remove everything */
101 		}
102 		if (access(arg, R_OK|W_OK|X_OK) != 0) {
103 			if (rmdir(arg) == 0)
104 				return (1);	/* salvaged: removed empty dir */
105 			if (!fflg) {
106 				fprintf(stderr, "rm: %s not changed\n", arg);
107 				errcode++;
108 			}
109 			return (0);		/* error */
110 		}
111 		if ((dirp = opendir(arg)) == NULL) {
112 			if (!fflg) {
113 				fprintf(stderr, "rm: cannot read %s?\n", arg);
114 				errcode++;
115 			}
116 			return (0);
117 		}
118 		if (level == 0)
119 			append(arg);
120 		prevname[0] = '\0';
121 		while ((dp = readdir(dirp)) != NULL) {
122 			if (isdot(dp->d_name)) {
123 				strcpy(prevname, dp->d_name);
124 				continue;
125 			}
126 			append(dp->d_name);
127 			closedir(dirp);
128 			ok = rm(path, level + 1);
129 			for (cp = pathp; *--cp != '/' && cp > path; )
130 				;
131 			pathp = cp;
132 			*cp++ = '\0';
133 			if ((dirp = opendir(arg)) == NULL) {
134 				if (!fflg) {
135 					fprintf(stderr, "rm: cannot read %s?\n", arg);
136 					errcode++;
137 				}
138 				break;
139 			}
140 			/* pick up where we left off */
141 			if (prevname[0] != '\0') {
142 				while ((dp = readdir(dirp)) != NULL &&
143 				    strcmp(prevname, dp->d_name) != 0)
144 					;
145 			}
146 			/* skip the one we just failed to delete */
147 			if (!ok) {
148 				dp = readdir(dirp);
149 				if (dp != NULL && strcmp(cp, dp->d_name)) {
150 					fprintf(stderr,
151 			"rm: internal synchronization error: %s, %s, %s\n",
152 						arg, cp, dp->d_name);
153 				}
154 				strcpy(prevname, dp->d_name);
155 			}
156 		}
157 		closedir(dirp);
158 		if (level == 0) {
159 			pathp = path;
160 			*pathp = '\0';
161 		}
162 		if (iflg) {
163 			printf("rm: remove %s? ", arg);
164 			if (!yes())
165 				return (0);
166 		}
167 		if (rmdir(arg) < 0) {
168 			if (!fflg || iflg) {
169 				fprintf(stderr, "rm: %s not removed\n", arg);
170 				errcode++;
171 			}
172 			return (0);
173 		}
174 		return (1);
175 	}
176 
177 	if (!fflg) {
178 		if ((buf.st_mode&S_IFMT) != S_IFLNK && access(arg, W_OK) < 0) {
179 			printf("rm: override protection %o for %s? ",
180 				buf.st_mode&0777, arg);
181 			if (!yes())
182 				return (0);
183 			goto rm;
184 		}
185 	}
186 	if (iflg) {
187 		printf("rm: remove %s? ", arg);
188 		if (!yes())
189 			return (0);
190 	}
191 rm:	if (unlink(arg) < 0) {
192 		if (!fflg || iflg) {
193 			fprintf(stderr, "rm: %s: ", arg);
194 			perror((char *)NULL);
195 			errcode++;
196 		}
197 		return (0);
198 	}
199 	return (1);
200 }
201 
202 /*
203  * Get a yes/no answer from the user.
204  */
205 yes()
206 {
207 	int i, b;
208 
209 	i = b = getchar();
210 	while (b != '\n' && b != EOF)
211 		b = getchar();
212 	return (i == 'y');
213 }
214 
215 /*
216  * Append 'name' to 'path'.
217  */
218 append(name)
219 	char *name;
220 {
221 	register int n;
222 
223 	n = strlen(name);
224 	if (path == NULL) {
225 		pathsz = MAXNAMLEN + MAXPATHLEN + 2;
226 		if ((path = malloc((u_int)pathsz)) == NULL) {
227 			fprintf(stderr, "rm: ran out of memory\n");
228 			exit(1);
229 		}
230 		pathp = path;
231 	} else if (pathp + n + 2 > path + pathsz) {
232 		fprintf(stderr, "rm: path name too long: %s\n", path);
233 		exit(1);
234 	} else if (pathp != path && pathp[-1] != '/')
235 		*pathp++ = '/';
236 	strcpy(pathp, name);
237 	pathp += n;
238 }
239 
240 usage()
241 {
242 	fputs("usage: rm [-rif] file ...\n", stderr);
243 	exit(1);
244 }
245