1 /*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include <dirent.h>
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14
15
16 // exported by the generic attribute support in libroot_build.so
17 extern "C" bool __get_attribute_dir_path(const struct stat* st,
18 const char* path, char* buffer);
19
20
21 class Path {
22 public:
Init(const char * path)23 bool Init(const char* path)
24 {
25 size_t len = strlen(path);
26 if (len == 0 || len >= PATH_MAX)
27 return false;
28
29 strcpy(fPath, path);
30 fPathLen = len;
31
32 return true;
33 }
34
GetPath() const35 const char* GetPath() const
36 {
37 return fPath;
38 }
39
Buffer()40 char* Buffer()
41 {
42 return fPath;
43 }
44
BufferChanged()45 void BufferChanged()
46 {
47 fPathLen = strlen(fPath);
48 }
49
PushLeaf(const char * leaf)50 bool PushLeaf(const char* leaf)
51 {
52 size_t leafLen = strlen(leaf);
53
54 int separatorLen = (fPath[fPathLen - 1] == '/' ? 0 : 1);
55 if (fPathLen + separatorLen + leafLen >= PATH_MAX)
56 return false;
57
58 if (separatorLen > 0)
59 fPath[fPathLen++] = '/';
60
61 strcpy(fPath + fPathLen, leaf);
62 fPathLen += leafLen;
63
64 return true;
65 }
66
PopLeaf()67 bool PopLeaf()
68 {
69 char* lastSlash = strrchr(fPath, '/');
70 if (lastSlash == NULL || lastSlash == fPath)
71 return false;
72
73 *lastSlash = '\0';
74 fPathLen = lastSlash - fPath;
75
76 return true;
77 }
78
79 char fPath[PATH_MAX];
80 size_t fPathLen;
81 };
82
83
84 static bool remove_entry(Path& entry, bool recursive, bool force,
85 bool removeAttributes);
86
87
88 static void
remove_dir_contents(Path & path,bool force,bool removeAttributes)89 remove_dir_contents(Path& path, bool force, bool removeAttributes)
90 {
91 // open the dir
92 DIR* dir = opendir(path.GetPath());
93 if (dir == NULL) {
94 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n",
95 path.GetPath(), strerror(errno));
96 return;
97 }
98
99 // iterate through the entries
100 errno = 0;
101 while (dirent* entry = readdir(dir)) {
102 // skip "." and ".."
103 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
104 continue;
105
106 if (!path.PushLeaf(entry->d_name)) {
107 fprintf(stderr, "Error: Path name of entry too long: dir: \"%s\", "
108 "entry: \"%s\"\n", path.GetPath(), entry->d_name);
109 continue;
110 }
111
112 remove_entry(path, true, force, removeAttributes);
113
114 path.PopLeaf();
115
116 errno = 0;
117 }
118
119 if (errno != 0) {
120 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
121 path.GetPath(), strerror(errno));
122 }
123
124 // close
125 closedir(dir);
126 }
127
128
129 static bool
remove_entry(Path & path,bool recursive,bool force,bool removeAttributes)130 remove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
131 {
132 // stat the file
133 struct stat st;
134 if (lstat(path.GetPath(), &st) < 0) {
135 // errno == 0 shouldn't happen, but found on OpenSUSE Linux 10.3
136 if (force && (errno == ENOENT || errno == 0))
137 return true;
138
139 fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", path.GetPath(),
140 strerror(errno));
141 return false;
142 }
143
144 // remove the file's attributes
145 if (removeAttributes) {
146 Path attrDirPath;
147 if (__get_attribute_dir_path(&st, path.GetPath(),
148 attrDirPath.Buffer())) {
149 attrDirPath.BufferChanged();
150 remove_entry(attrDirPath, true, true, false);
151 }
152 }
153
154 if (S_ISDIR(st.st_mode)) {
155 if (!recursive) {
156 fprintf(stderr, "Error: \"%s\" is a directory.\n", path.GetPath());
157 return false;
158 }
159
160 // remove the contents
161 remove_dir_contents(path, force, removeAttributes);
162
163 // remove the directory
164 if (rmdir(path.GetPath()) < 0) {
165 fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
166 path.GetPath(), strerror(errno));
167 return false;
168 }
169 } else {
170 // remove the entry
171 if (unlink(path.GetPath()) < 0) {
172 fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n",
173 path.GetPath(), strerror(errno));
174 return false;
175 }
176 }
177
178 return true;
179 }
180
181
182 int
main(int argc,const char * const * argv)183 main(int argc, const char* const* argv)
184 {
185 bool recursive = false;
186 bool force = false;
187
188 // parse parameters
189 int argi = 1;
190 for (argi = 1; argi < argc; argi++) {
191 const char *arg = argv[argi];
192 if (arg[0] != '-')
193 break;
194
195 if (arg[1] == '\0') {
196 fprintf(stderr, "Error: Invalid option \"-\"\n");
197 exit(1);
198 }
199
200 for (int i = 1; arg[i]; i++) {
201 switch (arg[i]) {
202 case 'f':
203 force = true;
204 break;
205 case 'r':
206 recursive = true;
207 break;
208 default:
209 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
210 exit(1);
211 }
212 }
213 }
214
215 // check params
216 if (argi >= argc) {
217 fprintf(stderr, "Usage: %s [ -rf ] <file>...\n", argv[0]);
218 exit(1);
219 }
220
221 // remove loop
222 for (; argi < argc; argi++) {
223 Path path;
224 if (!path.Init(argv[argi])) {
225 fprintf(stderr, "Error: Invalid path: \"%s\".\n", argv[argi]);
226 continue;
227 }
228
229 remove_entry(path, recursive, force, true);
230 }
231
232 return 0;
233 }
234