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: 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 35 const char* GetPath() const 36 { 37 return fPath; 38 } 39 40 char* Buffer() 41 { 42 return fPath; 43 } 44 45 void BufferChanged() 46 { 47 fPathLen = strlen(fPath); 48 } 49 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 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 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 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 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