doprnt.o is needed only on vax (need better way than this)
[unix-history] / usr / src / bin / rm / rm.c
... / ...
CommitLineData
1static char *sccsid = "@(#)rm.c 4.18 (Berkeley) %G%";
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
13int fflg; /* -f force - supress error messages */
14int iflg; /* -i interrogate user on each file */
15int rflg; /* -r recurse */
16
17int errcode; /* true if errors occured */
18
19char *strcpy(), *malloc(), *realloc();
20
21main(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
71char *path; /* pointer to malloc'ed buffer for path */
72char *pathp; /* current pointer to end of path */
73int pathsz; /* size of path */
74
75/*
76 * Return TRUE if sucessful. Recursive with -r (rflg)
77 */
78rm(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 */
212dotname(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 */
229yes()
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 */
242append(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}