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