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