10ba49c35SIngo Weinhold/*
20ba49c35SIngo Weinhold * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
30ba49c35SIngo Weinhold * Distributed under the terms of the MIT License.
40ba49c35SIngo Weinhold */
50ba49c35SIngo Weinhold
60ba49c35SIngo Weinhold#include <dirent.h>
70ba49c35SIngo Weinhold#include <errno.h>
80ba49c35SIngo Weinhold#include <limits.h>
90ba49c35SIngo Weinhold#include <stdio.h>
100ba49c35SIngo Weinhold#include <stdlib.h>
110ba49c35SIngo Weinhold#include <string.h>
120ba49c35SIngo Weinhold#include <sys/stat.h>
130ba49c35SIngo Weinhold#include <unistd.h>
140ba49c35SIngo Weinhold
150ba49c35SIngo Weinhold
160ba49c35SIngo Weinhold// exported by the generic attribute support in libroot_build.so
17bc96e8f3SIngo Weinholdextern "C" bool __get_attribute_dir_path(const struct stat* st,
18bc96e8f3SIngo Weinhold	const char* path, char* buffer);
190ba49c35SIngo Weinhold
200ba49c35SIngo Weinhold
210ba49c35SIngo Weinholdclass Path {
220ba49c35SIngo Weinholdpublic:
230ba49c35SIngo Weinhold	bool Init(const char* path)
240ba49c35SIngo Weinhold	{
250ba49c35SIngo Weinhold		size_t len = strlen(path);
260ba49c35SIngo Weinhold		if (len == 0 || len >= PATH_MAX)
270ba49c35SIngo Weinhold			return false;
280ba49c35SIngo Weinhold
290ba49c35SIngo Weinhold		strcpy(fPath, path);
300ba49c35SIngo Weinhold		fPathLen = len;
310ba49c35SIngo Weinhold
320ba49c35SIngo Weinhold		return true;
330ba49c35SIngo Weinhold	}
340ba49c35SIngo Weinhold
350ba49c35SIngo Weinhold	const char* GetPath() const
360ba49c35SIngo Weinhold	{
370ba49c35SIngo Weinhold		return fPath;
380ba49c35SIngo Weinhold	}
390ba49c35SIngo Weinhold
400ba49c35SIngo Weinhold	char* Buffer()
410ba49c35SIngo Weinhold	{
420ba49c35SIngo Weinhold		return fPath;
430ba49c35SIngo Weinhold	}
440ba49c35SIngo Weinhold
450ba49c35SIngo Weinhold	void BufferChanged()
460ba49c35SIngo Weinhold	{
470ba49c35SIngo Weinhold		fPathLen = strlen(fPath);
480ba49c35SIngo Weinhold	}
490ba49c35SIngo Weinhold
500ba49c35SIngo Weinhold	bool PushLeaf(const char* leaf)
510ba49c35SIngo Weinhold	{
520ba49c35SIngo Weinhold		size_t leafLen = strlen(leaf);
530ba49c35SIngo Weinhold
540ba49c35SIngo Weinhold		int separatorLen = (fPath[fPathLen - 1] == '/' ? 0 : 1);
550ba49c35SIngo Weinhold		if (fPathLen + separatorLen + leafLen >= PATH_MAX)
560ba49c35SIngo Weinhold			return false;
570ba49c35SIngo Weinhold
580ba49c35SIngo Weinhold		if (separatorLen > 0)
590ba49c35SIngo Weinhold			fPath[fPathLen++] = '/';
600ba49c35SIngo Weinhold
610ba49c35SIngo Weinhold		strcpy(fPath + fPathLen, leaf);
620ba49c35SIngo Weinhold		fPathLen += leafLen;
630ba49c35SIngo Weinhold
640ba49c35SIngo Weinhold		return true;
650ba49c35SIngo Weinhold	}
660ba49c35SIngo Weinhold
670ba49c35SIngo Weinhold	bool PopLeaf()
680ba49c35SIngo Weinhold	{
690ba49c35SIngo Weinhold		char* lastSlash = strrchr(fPath, '/');
700ba49c35SIngo Weinhold		if (lastSlash == NULL || lastSlash == fPath)
710ba49c35SIngo Weinhold			return false;
720ba49c35SIngo Weinhold
730ba49c35SIngo Weinhold		*lastSlash = '\0';
740ba49c35SIngo Weinhold		fPathLen = lastSlash - fPath;
750ba49c35SIngo Weinhold
760ba49c35SIngo Weinhold		return true;
770ba49c35SIngo Weinhold	}
780ba49c35SIngo Weinhold
790ba49c35SIngo Weinhold	char	fPath[PATH_MAX];
800ba49c35SIngo Weinhold	size_t	fPathLen;
810ba49c35SIngo Weinhold};
820ba49c35SIngo Weinhold
830ba49c35SIngo Weinhold
840ba49c35SIngo Weinholdstatic bool remove_entry(Path& entry, bool recursive, bool force,
850ba49c35SIngo Weinhold	bool removeAttributes);
860ba49c35SIngo Weinhold
870ba49c35SIngo Weinhold
880ba49c35SIngo Weinholdstatic void
890ba49c35SIngo Weinholdremove_dir_contents(Path& path, bool force, bool removeAttributes)
900ba49c35SIngo Weinhold{
910ba49c35SIngo Weinhold	// open the dir
920ba49c35SIngo Weinhold	DIR* dir = opendir(path.GetPath());
93599150a3SPhilippe Saint-Pierre	if (dir == NULL) {
940ba49c35SIngo Weinhold		fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n",
950ba49c35SIngo Weinhold			path.GetPath(), strerror(errno));
960ba49c35SIngo Weinhold		return;
970ba49c35SIngo Weinhold	}
980ba49c35SIngo Weinhold
990ba49c35SIngo Weinhold	// iterate through the entries
1000ba49c35SIngo Weinhold	errno = 0;
1010ba49c35SIngo Weinhold	while (dirent* entry = readdir(dir)) {
1020ba49c35SIngo Weinhold		// skip "." and ".."
1030ba49c35SIngo Weinhold		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
1040ba49c35SIngo Weinhold			continue;
1050ba49c35SIngo Weinhold
1060ba49c35SIngo Weinhold		if (!path.PushLeaf(entry->d_name)) {
1070ba49c35SIngo Weinhold			fprintf(stderr, "Error: Path name of entry too long: dir: \"%s\", "
1080ba49c35SIngo Weinhold				"entry: \"%s\"\n", path.GetPath(), entry->d_name);
1090ba49c35SIngo Weinhold			continue;
1100ba49c35SIngo Weinhold		}
1110ba49c35SIngo Weinhold
1120ba49c35SIngo Weinhold		remove_entry(path, true, force, removeAttributes);
1130ba49c35SIngo Weinhold
1140ba49c35SIngo Weinhold		path.PopLeaf();
1150ba49c35SIngo Weinhold
1160ba49c35SIngo Weinhold		errno = 0;
1170ba49c35SIngo Weinhold	}
1180ba49c35SIngo Weinhold
1190ba49c35SIngo Weinhold	if (errno != 0) {
1200ba49c35SIngo Weinhold		fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n",
1210ba49c35SIngo Weinhold			path.GetPath(), strerror(errno));
1220ba49c35SIngo Weinhold	}
1230ba49c35SIngo Weinhold
1240ba49c35SIngo Weinhold	// close
1250ba49c35SIngo Weinhold	closedir(dir);
1260ba49c35SIngo Weinhold}
1270ba49c35SIngo Weinhold
1280ba49c35SIngo Weinhold
1290ba49c35SIngo Weinholdstatic bool
1300ba49c35SIngo Weinholdremove_entry(Path& path, bool recursive, bool force, bool removeAttributes)
1310ba49c35SIngo Weinhold{
1320ba49c35SIngo Weinhold	// stat the file
1330ba49c35SIngo Weinhold	struct stat st;
1340ba49c35SIngo Weinhold	if (lstat(path.GetPath(), &st) < 0) {
1350ba49c35SIngo Weinhold		// errno == 0 shouldn't happen, but found on OpenSUSE Linux 10.3
1360ba49c35SIngo Weinhold		if (force && (errno == ENOENT || errno == 0))
1370ba49c35SIngo Weinhold			return true;
1380ba49c35SIngo Weinhold
1390ba49c35SIngo Weinhold		fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", path.GetPath(),
1400ba49c35SIngo Weinhold			strerror(errno));
1410ba49c35SIngo Weinhold		return false;
1420ba49c35SIngo Weinhold	}
1430ba49c35SIngo Weinhold
1440ba49c35SIngo Weinhold	// remove the file's attributes
1450ba49c35SIngo Weinhold	if (removeAttributes) {
1460ba49c35SIngo Weinhold		Path attrDirPath;
147bc96e8f3SIngo Weinhold		if (__get_attribute_dir_path(&st, path.GetPath(),
148bc96e8f3SIngo Weinhold				attrDirPath.Buffer())) {
1490ba49c35SIngo Weinhold			attrDirPath.BufferChanged();
1500ba49c35SIngo Weinhold			remove_entry(attrDirPath, true, true, false);
1510ba49c35SIngo Weinhold		}
1520ba49c35SIngo Weinhold	}
1530ba49c35SIngo Weinhold
1540ba49c35SIngo Weinhold	if (S_ISDIR(st.st_mode)) {
1550ba49c35SIngo Weinhold		if (!recursive) {
1560ba49c35SIngo Weinhold			fprintf(stderr, "Error: \"%s\" is a directory.\n", path.GetPath());
1570ba49c35SIngo Weinhold			return false;
1580ba49c35SIngo Weinhold		}
1590ba49c35SIngo Weinhold
1600ba49c35SIngo Weinhold		// remove the contents
1610ba49c35SIngo Weinhold		remove_dir_contents(path, force, removeAttributes);
1620ba49c35SIngo Weinhold
1630ba49c35SIngo Weinhold		// remove the directory
1640ba49c35SIngo Weinhold		if (rmdir(path.GetPath()) < 0) {
1650ba49c35SIngo Weinhold			fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
1660ba49c35SIngo Weinhold				path.GetPath(), strerror(errno));
1670ba49c35SIngo Weinhold			return false;
1680ba49c35SIngo Weinhold		}
1690ba49c35SIngo Weinhold	} else {
1700ba49c35SIngo Weinhold		// remove the entry
1710ba49c35SIngo Weinhold		if (unlink(path.GetPath()) < 0) {
1720ba49c35SIngo Weinhold			fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n",
1730ba49c35SIngo Weinhold				path.GetPath(), strerror(errno));
1740ba49c35SIngo Weinhold			return false;
1750ba49c35SIngo Weinhold		}
1760ba49c35SIngo Weinhold	}
1770ba49c35SIngo Weinhold
1780ba49c35SIngo Weinhold	return true;
1790ba49c35SIngo Weinhold}
1800ba49c35SIngo Weinhold
1810ba49c35SIngo Weinhold
1820ba49c35SIngo Weinholdint
1830ba49c35SIngo Weinholdmain(int argc, const char* const* argv)
1840ba49c35SIngo Weinhold{
1850ba49c35SIngo Weinhold	bool recursive = false;
1860ba49c35SIngo Weinhold	bool force = false;
1870ba49c35SIngo Weinhold
1880ba49c35SIngo Weinhold	// parse parameters
1890ba49c35SIngo Weinhold	int argi = 1;
1900ba49c35SIngo Weinhold	for (argi = 1; argi < argc; argi++) {
1910ba49c35SIngo Weinhold		const char *arg = argv[argi];
1920ba49c35SIngo Weinhold		if (arg[0] != '-')
1930ba49c35SIngo Weinhold			break;
1940ba49c35SIngo Weinhold
1950ba49c35SIngo Weinhold		if (arg[1] == '\0') {
1960ba49c35SIngo Weinhold			fprintf(stderr, "Error: Invalid option \"-\"\n");
1970ba49c35SIngo Weinhold			exit(1);
1980ba49c35SIngo Weinhold		}
1990ba49c35SIngo Weinhold
2000ba49c35SIngo Weinhold		for (int i = 1; arg[i]; i++) {
2010ba49c35SIngo Weinhold			switch (arg[i]) {
2020ba49c35SIngo Weinhold				case 'f':
2030ba49c35SIngo Weinhold					force = true;
2040ba49c35SIngo Weinhold					break;
2050ba49c35SIngo Weinhold				case 'r':
2060ba49c35SIngo Weinhold					recursive = true;
2070ba49c35SIngo Weinhold					break;
2080ba49c35SIngo Weinhold				default:
2090ba49c35SIngo Weinhold					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
2100ba49c35SIngo Weinhold					exit(1);
2110ba49c35SIngo Weinhold			}
2120ba49c35SIngo Weinhold		}
2130ba49c35SIngo Weinhold	}
2140ba49c35SIngo Weinhold
2150ba49c35SIngo Weinhold	// check params
2160ba49c35SIngo Weinhold	if (argi >= argc) {
2170ba49c35SIngo Weinhold		fprintf(stderr, "Usage: %s [ -rf ] <file>...\n", argv[0]);
2180ba49c35SIngo Weinhold		exit(1);
2190ba49c35SIngo Weinhold	}
2200ba49c35SIngo Weinhold
2210ba49c35SIngo Weinhold	// remove loop
2220ba49c35SIngo Weinhold	for (; argi < argc; argi++) {
2230ba49c35SIngo Weinhold		Path path;
2240ba49c35SIngo Weinhold		if (!path.Init(argv[argi])) {
2250ba49c35SIngo Weinhold			fprintf(stderr, "Error: Invalid path: \"%s\".\n", argv[argi]);
2260ba49c35SIngo Weinhold			continue;
2270ba49c35SIngo Weinhold		}
2280ba49c35SIngo Weinhold
2290ba49c35SIngo Weinhold		remove_entry(path, recursive, force, true);
2300ba49c35SIngo Weinhold	}
2310ba49c35SIngo Weinhold
2320ba49c35SIngo Weinhold	return 0;
2330ba49c35SIngo Weinhold}